diff --git a/resources/sql/patches/112.oauthaccesscoderedirecturi.sql b/resources/sql/patches/112.oauthaccesscoderedirecturi.sql
new file mode 100644
index 0000000000..f151c438ad
--- /dev/null
+++ b/resources/sql/patches/112.oauthaccesscoderedirecturi.sql
@@ -0,0 +1,3 @@
+ALTER TABLE `phabricator_oauth_server`.`oauth_server_oauthserverauthorizationcode`
+  ADD `redirectURI` varchar(255) NOT NULL
+
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
index da22898fe2..bdd1fdebe0 100644
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -1,1612 +1,1614 @@
 <?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',
     'Aphront403Response' => 'aphront/response/403',
     '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',
     'AphrontDatabaseTransactionState' => 'storage/transaction',
     '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',
     'AphrontFormRadioButtonControl' => 'view/form/control/radio',
     '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',
     'AphrontHTTPSink' => 'aphront/sink/base',
     'AphrontHTTPSinkTestCase' => 'aphront/sink/base/__tests__',
     'AphrontHeadsupActionListView' => 'view/layout/headsup/actionlist',
     'AphrontHeadsupActionView' => 'view/layout/headsup/action',
     'AphrontIsolatedDatabaseConnection' => 'storage/connection/isolated',
     'AphrontIsolatedDatabaseConnectionTestCase' => 'storage/connection/isolated/__tests__',
     'AphrontIsolatedHTTPSink' => 'aphront/sink/test',
     'AphrontJSONResponse' => 'aphront/response/json',
     'AphrontJavelinView' => 'view/javelin-view',
     'AphrontKeyboardShortcutsAvailableView' => 'view/widget/keyboardshortcuts',
     'AphrontListFilterView' => 'view/layout/listfilter',
     'AphrontMiniPanelView' => 'view/layout/minipanel',
     'AphrontMySQLDatabaseConnection' => 'storage/connection/mysql',
     'AphrontMySQLDatabaseConnectionTestCase' => 'storage/connection/mysql/__tests__',
     'AphrontNullView' => 'view/null',
     'AphrontPHPHTTPSink' => 'aphront/sink/php',
     'AphrontPageView' => 'view/page/base',
     'AphrontPagerView' => 'view/control/pager',
     'AphrontPanelView' => 'view/layout/panel',
     'AphrontPlainTextResponse' => 'aphront/response/plaintext',
     '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',
     'AphrontRequestTestCase' => 'aphront/request/__tests__',
     'AphrontResponse' => 'aphront/response/base',
     'AphrontScopedUnguardedWriteCapability' => 'aphront/writeguard/scopeguard',
     'AphrontSideNavFilterView' => 'view/layout/sidenavfilter',
     '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',
     'AphrontWriteGuard' => 'aphront/writeguard',
     'CelerityAPI' => 'infrastructure/celerity/api',
     'CelerityResourceController' => 'infrastructure/celerity/controller',
     'CelerityResourceGraph' => 'infrastructure/celerity/graph',
     'CelerityResourceMap' => 'infrastructure/celerity/map',
     'CelerityStaticResourceResponse' => 'infrastructure/celerity/response',
     'ConduitAPIMethod' => 'applications/conduit/method/base',
     'ConduitAPIRequest' => 'applications/conduit/protocol/request',
     'ConduitAPIResponse' => 'applications/conduit/protocol/response',
     'ConduitAPI_arcanist_Method' => 'applications/conduit/method/arcanist/base',
     'ConduitAPI_arcanist_projectinfo_Method' => 'applications/conduit/method/arcanist/projectinfo',
     'ConduitAPI_chatlog_Method' => 'applications/conduit/method/chatlog/base',
     'ConduitAPI_chatlog_query_Method' => 'applications/conduit/method/chatlog/query',
     'ConduitAPI_chatlog_record_Method' => 'applications/conduit/method/chatlog/record',
     '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_createcomment_Method' => 'applications/conduit/method/differential/createcomment',
     '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_getrevisioncomments_Method' => 'applications/conduit/method/differential/getrevisioncomments',
     '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_query_Method' => 'applications/conduit/method/differential/query',
     '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_findsymbols_Method' => 'applications/conduit/method/diffusion/findsymbols',
     '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_feed_query_Method' => 'applications/conduit/method/feed/query',
     '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_Method' => 'applications/conduit/method/maniphest/base',
     'ConduitAPI_maniphest_createtask_Method' => 'applications/conduit/method/maniphest/createtask',
     'ConduitAPI_maniphest_find_Method' => 'applications/conduit/method/maniphest/find',
     'ConduitAPI_maniphest_gettasktransactions_Method' => 'applications/conduit/method/maniphest/gettasktransactions',
     'ConduitAPI_maniphest_info_Method' => 'applications/conduit/method/maniphest/info',
     'ConduitAPI_maniphest_update_Method' => 'applications/conduit/method/maniphest/update',
     '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_phid_Method' => 'applications/conduit/method/phid/base',
     'ConduitAPI_phid_info_Method' => 'applications/conduit/method/phid/info',
     'ConduitAPI_phid_query_Method' => 'applications/conduit/method/phid/query',
     'ConduitAPI_phriction_Method' => 'applications/conduit/method/phriction/base',
     'ConduitAPI_phriction_edit_Method' => 'applications/conduit/method/phriction/edit',
     'ConduitAPI_phriction_history_Method' => 'applications/conduit/method/phriction/history',
     'ConduitAPI_phriction_info_Method' => 'applications/conduit/method/phriction/info',
     'ConduitAPI_project_Method' => 'applications/conduit/method/project/base',
     'ConduitAPI_project_query_Method' => 'applications/conduit/method/project/query',
     'ConduitAPI_remarkup_process_Method' => 'applications/conduit/method/remarkup/process',
     'ConduitAPI_slowvote_info_Method' => 'applications/conduit/method/slowvote/info',
     'ConduitAPI_user_Method' => 'applications/conduit/method/user/base',
     'ConduitAPI_user_find_Method' => 'applications/conduit/method/user/find',
     'ConduitAPI_user_info_Method' => 'applications/conduit/method/user/info',
     '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',
     'DarkConsoleEventPlugin' => 'aphront/console/plugin/event',
     'DarkConsoleEventPluginAPI' => 'aphront/console/plugin/event/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',
     'DifferentialActionHasNoEffectException' => 'applications/differential/exception/noeffect',
     'DifferentialAddCommentView' => 'applications/differential/view/addcomment',
     'DifferentialAffectedPath' => 'applications/differential/storage/affectedpath',
     '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',
     'DifferentialBranchFieldSpecification' => 'applications/differential/field/specification/branch',
     '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',
     'DifferentialCommitsFieldSpecification' => 'applications/differential/field/specification/commits',
     'DifferentialController' => 'applications/differential/controller/base',
     'DifferentialDAO' => 'applications/differential/storage/base',
     'DifferentialDateCreatedFieldSpecification' => 'applications/differential/field/specification/datecreated',
     'DifferentialDateModifiedFieldSpecification' => 'applications/differential/field/specification/datemodified',
     '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',
     'DifferentialException' => 'applications/differential/exception/base',
     'DifferentialExceptionMail' => 'applications/differential/mail/exception',
     'DifferentialExportPatchFieldSpecification' => 'applications/differential/field/specification/exportpatch',
     'DifferentialFieldDataNotAvailableException' => 'applications/differential/field/exception/notavailable',
     'DifferentialFieldParseException' => 'applications/differential/field/exception/parse',
     'DifferentialFieldSelector' => 'applications/differential/field/selector/base',
     'DifferentialFieldSpecification' => 'applications/differential/field/specification/base',
     'DifferentialFieldSpecificationIncompleteException' => 'applications/differential/field/exception/incomplete',
     'DifferentialFieldValidationException' => 'applications/differential/field/exception/validation',
     'DifferentialGitSVNIDFieldSpecification' => 'applications/differential/field/specification/gitsvnid',
     'DifferentialHostFieldSpecification' => 'applications/differential/field/specification/host',
     'DifferentialHunk' => 'applications/differential/storage/hunk',
     'DifferentialInlineComment' => 'applications/differential/storage/inlinecomment',
     'DifferentialInlineCommentEditController' => 'applications/differential/controller/inlinecommentedit',
     'DifferentialInlineCommentEditView' => 'applications/differential/view/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',
     'DifferentialLocalCommitsView' => 'applications/differential/view/localcommits',
     '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',
     'DifferentialReviewedByFieldSpecification' => 'applications/differential/field/specification/reviewedby',
     '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',
     'DifferentialRevisionIDFieldParserTestCase' => 'applications/differential/field/specification/revisionid/__tests__',
     'DifferentialRevisionIDFieldSpecification' => 'applications/differential/field/specification/revisionid',
     'DifferentialRevisionListController' => 'applications/differential/controller/revisionlist',
     'DifferentialRevisionListData' => 'applications/differential/data/revisionlist',
     'DifferentialRevisionListView' => 'applications/differential/view/revisionlist',
     'DifferentialRevisionQuery' => 'applications/differential/query/revision',
     'DifferentialRevisionStatsController' => 'applications/differential/controller/revisionstats',
     'DifferentialRevisionStatsView' => 'applications/differential/view/revisionstats',
     'DifferentialRevisionStatusFieldSpecification' => 'applications/differential/field/specification/revisionstatus',
     'DifferentialRevisionUpdateHistoryView' => 'applications/differential/view/revisionupdatehistory',
     'DifferentialRevisionViewController' => 'applications/differential/controller/revisionview',
     'DifferentialSubscribeController' => 'applications/differential/controller/subscribe',
     'DifferentialSummaryFieldSpecification' => 'applications/differential/field/specification/summary',
     'DifferentialTasksAttacher' => 'applications/differential/tasks',
     'DifferentialTestPlanFieldSpecification' => 'applications/differential/field/specification/testplan',
     'DifferentialTitleFieldSpecification' => 'applications/differential/field/specification/title',
     'DifferentialUnitFieldSpecification' => 'applications/differential/field/specification/unit',
     'DifferentialUnitStatus' => 'applications/differential/constants/unitstatus',
     'DifferentialUnitTestResult' => 'applications/differential/constants/unittestresult',
     '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',
     'DiffusionCommentListView' => 'applications/diffusion/view/commentlist',
     'DiffusionCommentView' => 'applications/diffusion/view/comment',
     'DiffusionCommitChangeTableView' => 'applications/diffusion/view/commitchangetable',
     'DiffusionCommitController' => 'applications/diffusion/controller/commit',
     'DiffusionContainsQuery' => 'applications/diffusion/query/contains/base',
     'DiffusionController' => 'applications/diffusion/controller/base',
     'DiffusionDiffController' => 'applications/diffusion/controller/diff',
     'DiffusionDiffQuery' => 'applications/diffusion/query/diff/base',
     'DiffusionEmptyResultView' => 'applications/diffusion/view/emptyresult',
     'DiffusionFileContent' => 'applications/diffusion/data/filecontent',
     'DiffusionFileContentQuery' => 'applications/diffusion/query/filecontent/base',
     'DiffusionGitBranchQuery' => 'applications/diffusion/query/branch/git',
     'DiffusionGitBranchQueryTestCase' => 'applications/diffusion/query/branch/git/__tests__',
     'DiffusionGitBrowseQuery' => 'applications/diffusion/query/browse/git',
     'DiffusionGitContainsQuery' => 'applications/diffusion/query/contains/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',
     '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',
     'DiffusionMercurialBranchQuery' => 'applications/diffusion/query/branch/mercurial',
     'DiffusionMercurialBrowseQuery' => 'applications/diffusion/query/browse/mercurial',
     'DiffusionMercurialContainsQuery' => 'applications/diffusion/query/contains/mercurial',
     'DiffusionMercurialDiffQuery' => 'applications/diffusion/query/diff/mercurial',
     'DiffusionMercurialFileContentQuery' => 'applications/diffusion/query/filecontent/mercurial',
     'DiffusionMercurialHistoryQuery' => 'applications/diffusion/query/history/mercurial',
     'DiffusionMercurialLastModifiedQuery' => 'applications/diffusion/query/lastmodified/mercurial',
     'DiffusionMercurialRequest' => 'applications/diffusion/request/mercurial',
     'DiffusionPathChange' => 'applications/diffusion/data/pathchange',
     'DiffusionPathChangeQuery' => 'applications/diffusion/query/pathchange/base',
     'DiffusionPathCompleteController' => 'applications/diffusion/controller/pathcomplete',
     'DiffusionPathIDQuery' => 'applications/diffusion/query/pathid/base',
     'DiffusionPathQueryTestCase' => 'applications/diffusion/query/pathid/base/__tests__',
     'DiffusionPathValidateController' => 'applications/diffusion/controller/pathvalidate',
     'DiffusionQuery' => 'applications/diffusion/query/base',
     'DiffusionRepositoryController' => 'applications/diffusion/controller/repository',
     'DiffusionRepositoryPath' => 'applications/diffusion/data/repositorypath',
     'DiffusionRequest' => 'applications/diffusion/request/base',
     'DiffusionSVNContainsQuery' => 'applications/diffusion/query/contains/svn',
     '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',
     'DiffusionSymbolController' => 'applications/diffusion/controller/symbol',
     'DiffusionSymbolQuery' => 'applications/diffusion/query/symbol',
     'DiffusionView' => 'applications/diffusion/view/base',
     'DrydockAllocator' => 'applications/drydock/allocator/resource',
     'DrydockAllocatorWorker' => 'applications/drydock/allocator/worker',
     'DrydockBlueprint' => 'applications/drydock/blueprint/base',
     'DrydockCommandInterface' => 'applications/drydock/interface/command/base',
     'DrydockConstants' => 'applications/drydock/constants/base',
     'DrydockController' => 'applications/drydock/controller/base',
     'DrydockDAO' => 'applications/drydock/storage/base',
     'DrydockEC2HostBlueprint' => 'applications/drydock/blueprint/ec2host',
     'DrydockInterface' => 'applications/drydock/interface/base',
     'DrydockLease' => 'applications/drydock/storage/lease',
     'DrydockLeaseListController' => 'applications/drydock/controller/leaselist',
     'DrydockLeaseStatus' => 'applications/drydock/constants/leasestatus',
     'DrydockLocalCommandInterface' => 'applications/drydock/interface/command/local',
     'DrydockLocalHostBlueprint' => 'applications/drydock/blueprint/localhost',
     'DrydockRemoteHostBlueprint' => 'applications/drydock/blueprint/remotehost',
     'DrydockResource' => 'applications/drydock/storage/resource',
     'DrydockResourceAllocateController' => 'applications/drydock/controller/resourceallocate',
     'DrydockResourceListController' => 'applications/drydock/controller/resourcelist',
     'DrydockResourceStatus' => 'applications/drydock/constants/resourcestatus',
     'DrydockSSHCommandInterface' => 'applications/drydock/interface/command/ssh',
     'HeraldAction' => 'applications/herald/storage/action',
     'HeraldActionConfig' => 'applications/herald/config/action',
     'HeraldAllRulesController' => 'applications/herald/controller/all',
     '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',
     'HeraldRuleEdit' => 'applications/herald/storage/edithistory',
     'HeraldRuleEditHistoryController' => 'applications/herald/controller/edithistory',
     'HeraldRuleEditHistoryView' => 'applications/herald/view/edithistory',
     'HeraldRuleListView' => 'applications/herald/view/rulelist',
     'HeraldRuleTranscript' => 'applications/herald/storage/transcript/rule',
     'HeraldRuleTypeConfig' => 'applications/herald/config/ruletype',
     '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',
     'JavelinReactorExample' => 'applications/uiexample/examples/reactor',
     'JavelinViewExample' => 'applications/uiexample/examples/client',
     'JavelinViewExampleServerView' => 'applications/uiexample/examples/client',
     'LiskDAO' => 'storage/lisk/dao',
     'LiskIsolationTestCase' => 'storage/lisk/dao/__tests__',
     'LiskIsolationTestDAO' => 'storage/lisk/dao/__tests__',
     'LiskIsolationTestDAOException' => 'storage/lisk/dao/__tests__',
     'ManiphestAction' => 'applications/maniphest/constants/action',
     'ManiphestAuxiliaryFieldDefaultSpecification' => 'applications/maniphest/auxiliaryfield/default',
     'ManiphestAuxiliaryFieldSpecification' => 'applications/maniphest/auxiliaryfield/base',
     'ManiphestAuxiliaryFieldTypeException' => 'applications/maniphest/auxiliaryfield/typeexception',
     'ManiphestAuxiliaryFieldValidationException' => 'applications/maniphest/auxiliaryfield/validationexception',
     'ManiphestBatchEditController' => 'applications/maniphest/controller/batch',
     'ManiphestConstants' => 'applications/maniphest/constants/base',
     'ManiphestController' => 'applications/maniphest/controller/base',
     'ManiphestDAO' => 'applications/maniphest/storage/base',
     'ManiphestDefaultTaskExtensions' => 'applications/maniphest/extensions/task',
     'ManiphestExportController' => 'applications/maniphest/controller/export',
     'ManiphestReplyHandler' => 'applications/maniphest/replyhandler',
     'ManiphestReportController' => 'applications/maniphest/controller/report',
     'ManiphestTask' => 'applications/maniphest/storage/task',
     'ManiphestTaskAuxiliaryStorage' => 'applications/maniphest/storage/auxiliary',
     'ManiphestTaskDescriptionChangeController' => 'applications/maniphest/controller/descriptionchange',
     'ManiphestTaskDescriptionDiffController' => 'applications/maniphest/controller/descriptiondiff',
     'ManiphestTaskDescriptionPreviewController' => 'applications/maniphest/controller/descriptionpreview',
     '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',
     'MetaMTAConstants' => 'applications/metamta/constants/base',
     'MetaMTANotificationType' => 'applications/metamta/constants/notificationtype',
     'Phabricator404Controller' => 'applications/base/controller/404',
     'PhabricatorAuditActionConstants' => 'applications/audit/constants/action',
     'PhabricatorAuditAddCommentController' => 'applications/audit/controller/addcomment',
     'PhabricatorAuditComment' => 'applications/audit/storage/auditcomment',
     'PhabricatorAuditCommentEditor' => 'applications/audit/editor/comment',
     'PhabricatorAuditCommitListView' => 'applications/audit/view/commitlist',
     'PhabricatorAuditCommitQuery' => 'applications/audit/query/commit',
     'PhabricatorAuditCommitStatusConstants' => 'applications/audit/constants/commitstatus',
     'PhabricatorAuditController' => 'applications/audit/controller/base',
     'PhabricatorAuditDAO' => 'applications/audit/storage/base',
     'PhabricatorAuditListController' => 'applications/audit/controller/list',
     'PhabricatorAuditListView' => 'applications/audit/view/list',
     'PhabricatorAuditPreviewController' => 'applications/audit/controller/preview',
     'PhabricatorAuditQuery' => 'applications/audit/query/audit',
     'PhabricatorAuditReplyHandler' => 'applications/audit/replyhandler',
     'PhabricatorAuditStatusConstants' => 'applications/audit/constants/status',
     'PhabricatorAuthController' => 'applications/auth/controller/base',
     'PhabricatorCalendarBrowseController' => 'applications/calendar/controller/browse',
     'PhabricatorCalendarController' => 'applications/calendar/controller/base',
     'PhabricatorChatLogChannelListController' => 'applications/chatlog/controller/channellist',
     'PhabricatorChatLogChannelLogController' => 'applications/chatlog/controller/channellog',
     'PhabricatorChatLogConstants' => 'applications/chatlog/constants/base',
     'PhabricatorChatLogController' => 'applications/chatlog/controller/base',
     'PhabricatorChatLogDAO' => 'applications/chatlog/storage/base',
     'PhabricatorChatLogEvent' => 'applications/chatlog/storage/event',
     'PhabricatorChatLogEventType' => 'applications/chatlog/constants/eventtype',
     'PhabricatorChatLogQuery' => 'applications/chatlog/query',
     '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',
     'PhabricatorContentSource' => 'applications/metamta/contentsource/source',
     'PhabricatorContentSourceView' => 'applications/metamta/contentsource/view',
     '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',
     'PhabricatorDirectoryCategoryViewController' => 'applications/directory/controller/categoryview',
     'PhabricatorDirectoryController' => 'applications/directory/controller/base',
     'PhabricatorDirectoryDAO' => 'applications/directory/storage/base',
     'PhabricatorDirectoryEditController' => 'applications/directory/controller/edit',
     'PhabricatorDirectoryItem' => 'applications/directory/storage/item',
     'PhabricatorDirectoryItemDeleteController' => 'applications/directory/controller/itemdelete',
     'PhabricatorDirectoryItemEditController' => 'applications/directory/controller/itemedit',
     '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',
     'PhabricatorEnvTestCase' => 'infrastructure/env/__tests__',
     'PhabricatorEvent' => 'infrastructure/events/event',
     'PhabricatorEventEngine' => 'infrastructure/events/engine',
     'PhabricatorEventType' => 'infrastructure/events/constant/type',
     'PhabricatorFeedBuilder' => 'applications/feed/builder/feed',
     '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',
     'PhabricatorFeedStoryAudit' => 'applications/feed/story/audit',
     'PhabricatorFeedStoryData' => 'applications/feed/storage/story',
     'PhabricatorFeedStoryDifferential' => 'applications/feed/story/differential',
     'PhabricatorFeedStoryManiphest' => 'applications/feed/story/maniphest',
     'PhabricatorFeedStoryPhriction' => 'applications/feed/story/phriction',
     'PhabricatorFeedStoryProject' => 'applications/feed/story/project',
     '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',
     'PhabricatorFeedView' => 'applications/feed/view/base',
     'PhabricatorFile' => 'applications/files/storage/file',
     'PhabricatorFileController' => 'applications/files/controller/base',
     'PhabricatorFileDAO' => 'applications/files/storage/base',
     'PhabricatorFileDataController' => 'applications/files/controller/data',
     'PhabricatorFileDeleteController' => 'applications/files/controller/delete',
     'PhabricatorFileDropUploadController' => 'applications/files/controller/dropupload',
     'PhabricatorFileImageMacro' => 'applications/files/storage/imagemacro',
     'PhabricatorFileInfoController' => 'applications/files/controller/info',
     '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',
     'PhabricatorFileSideNavView' => 'applications/files/view/sidenav',
     'PhabricatorFileStorageBlob' => 'applications/files/storage/storageblob',
     'PhabricatorFileStorageEngine' => 'applications/files/engine/base',
     'PhabricatorFileStorageEngineSelector' => 'applications/files/engineselector/base',
     'PhabricatorFileTransformController' => 'applications/files/controller/transform',
     'PhabricatorFileUploadController' => 'applications/files/controller/upload',
     'PhabricatorFileUploadException' => 'applications/files/exception/upload',
     'PhabricatorFileUploadView' => 'applications/files/view/upload',
     'PhabricatorGarbageCollectorDaemon' => 'infrastructure/daemon/garbagecollector',
     'PhabricatorGoodForNothingWorker' => 'infrastructure/daemon/workers/worker/goodfornothing',
     'PhabricatorHandleObjectSelectorDataView' => 'applications/phid/handle/view/selector',
     'PhabricatorHash' => 'infrastructure/util/hash',
     'PhabricatorHelpController' => 'applications/help/controller/base',
     'PhabricatorHelpKeyboardShortcutController' => 'applications/help/controller/keyboardshortcut',
     'PhabricatorIRCBot' => 'infrastructure/daemon/irc/bot',
     'PhabricatorIRCDifferentialNotificationHandler' => 'infrastructure/daemon/irc/handler/differentialnotification',
     'PhabricatorIRCHandler' => 'infrastructure/daemon/irc/handler/base',
     'PhabricatorIRCLogHandler' => 'infrastructure/daemon/irc/handler/log',
     'PhabricatorIRCMessage' => 'infrastructure/daemon/irc/message',
     'PhabricatorIRCObjectNameHandler' => 'infrastructure/daemon/irc/handler/objectname',
     'PhabricatorIRCProtocolHandler' => 'infrastructure/daemon/irc/handler/protocol',
     'PhabricatorIRCWhatsNewHandler' => 'infrastructure/daemon/irc/handler/whatsnew',
     'PhabricatorImageTransformer' => 'applications/files/transform',
     'PhabricatorInfrastructureTestCase' => 'infrastructure/__tests__',
     'PhabricatorJavelinLinter' => 'infrastructure/lint/linter/javelin',
     'PhabricatorLintEngine' => 'infrastructure/lint/engine',
     'PhabricatorLiskDAO' => 'applications/base/storage/lisk',
     'PhabricatorLocalDiskFileStorageEngine' => 'applications/files/engine/localdisk',
     'PhabricatorLocalTimeTestCase' => 'view/utils/__tests__',
     'PhabricatorLoginController' => 'applications/auth/controller/login',
     'PhabricatorLoginValidateController' => 'applications/auth/controller/validate',
     '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',
     'PhabricatorMetaMTAAttachment' => 'applications/metamta/storage/mail',
     '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',
     'PhabricatorOAuthClientAuthorization' => 'applications/oauthserver/storage/clientauthorization',
     'PhabricatorOAuthClientAuthorizationBaseController' => 'applications/oauthserver/controller/clientauthorization/base',
     'PhabricatorOAuthClientAuthorizationDeleteController' => 'applications/oauthserver/controller/clientauthorization/delete',
     'PhabricatorOAuthClientAuthorizationEditController' => 'applications/oauthserver/controller/clientauthorization/edit',
     'PhabricatorOAuthClientAuthorizationListController' => 'applications/oauthserver/controller/clientauthorization/list',
     'PhabricatorOAuthClientBaseController' => 'applications/oauthserver/controller/client/base',
     'PhabricatorOAuthClientDeleteController' => 'applications/oauthserver/controller/client/delete',
     'PhabricatorOAuthClientEditController' => 'applications/oauthserver/controller/client/edit',
     'PhabricatorOAuthClientListController' => 'applications/oauthserver/controller/client/list',
     'PhabricatorOAuthClientViewController' => 'applications/oauthserver/controller/client/view',
     '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',
     'PhabricatorOAuthProviderGoogle' => 'applications/auth/oauth/provider/google',
     'PhabricatorOAuthProviderPhabricator' => 'applications/auth/oauth/provider/phabricator',
     'PhabricatorOAuthRegistrationController' => 'applications/auth/controller/oauthregistration/base',
     'PhabricatorOAuthResponse' => 'applications/oauthserver/response',
     'PhabricatorOAuthServer' => 'applications/oauthserver/server',
     'PhabricatorOAuthServerAccessToken' => 'applications/oauthserver/storage/accesstoken',
     'PhabricatorOAuthServerAuthController' => 'applications/oauthserver/controller/auth',
     'PhabricatorOAuthServerAuthorizationCode' => 'applications/oauthserver/storage/authorizationcode',
     'PhabricatorOAuthServerClient' => 'applications/oauthserver/storage/client',
     'PhabricatorOAuthServerController' => 'applications/oauthserver/controller/base',
     'PhabricatorOAuthServerDAO' => 'applications/oauthserver/storage/base',
     'PhabricatorOAuthServerScope' => 'applications/oauthserver/scope',
+    'PhabricatorOAuthServerTestCase' => 'applications/oauthserver/server/__tests__',
     'PhabricatorOAuthServerTestController' => 'applications/oauthserver/controller/test',
     'PhabricatorOAuthServerTokenController' => 'applications/oauthserver/controller/token',
     'PhabricatorOAuthUnlinkController' => 'applications/auth/controller/unlink',
     'PhabricatorObjectAttachmentEditor' => 'applications/search/editor/attach',
     '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',
     'PhabricatorOwnerPathQuery' => 'applications/owners/query/path',
     'PhabricatorOwnerRelatedListController' => 'applications/owners/controller/relatedlist',
     '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',
     'PhabricatorOwnersPackageCommitRelationship' => 'applications/owners/storage/packagecommitrelationship',
     'PhabricatorOwnersPath' => 'applications/owners/storage/path',
     'PhabricatorPHID' => 'applications/phid/storage/phid',
     'PhabricatorPHIDConstants' => 'applications/phid/constants',
     'PhabricatorPHIDController' => 'applications/phid/controller/base',
     'PhabricatorPHIDDAO' => 'applications/phid/storage/base',
     'PhabricatorPHIDLookupController' => 'applications/phid/controller/lookup',
     'PhabricatorPaste' => 'applications/paste/storage/paste',
     'PhabricatorPasteController' => 'applications/paste/controller/base',
     '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',
     'PhabricatorProfileHeaderView' => 'view/layout/profileheader',
     'PhabricatorProject' => 'applications/project/storage/project',
     'PhabricatorProjectAffiliation' => 'applications/project/storage/affiliation',
     'PhabricatorProjectConstants' => 'applications/project/constants/base',
     'PhabricatorProjectController' => 'applications/project/controller/base',
     'PhabricatorProjectCreateController' => 'applications/project/controller/create',
     'PhabricatorProjectDAO' => 'applications/project/storage/base',
     'PhabricatorProjectEditor' => 'applications/project/editor/project',
     'PhabricatorProjectListController' => 'applications/project/controller/list',
     'PhabricatorProjectNameCollisionException' => 'applications/project/exception/namecollison',
     'PhabricatorProjectProfile' => 'applications/project/storage/profile',
     'PhabricatorProjectProfileController' => 'applications/project/controller/profile',
     'PhabricatorProjectProfileEditController' => 'applications/project/controller/profileedit',
     'PhabricatorProjectQuery' => 'applications/project/query/project',
     'PhabricatorProjectQueryUtil' => 'applications/project/query/util',
     'PhabricatorProjectStatus' => 'applications/project/constants/status',
     'PhabricatorProjectSubproject' => 'applications/project/storage/subproject',
     'PhabricatorProjectTransaction' => 'applications/project/storage/transaction',
     'PhabricatorProjectTransactionType' => 'applications/project/constants/transaction',
     'PhabricatorProjectUpdateController' => 'applications/project/controller/update',
     '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',
     'PhabricatorRepositoryCommitOwnersWorker' => 'applications/repository/worker/owner',
     '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',
     'PhabricatorRepositoryGitCommitDiscoveryDaemonTestCase' => 'applications/repository/daemon/commitdiscovery/git/__tests__',
     'PhabricatorRepositoryGitCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/git',
     'PhabricatorRepositoryGitFetchDaemon' => 'applications/repository/daemon/gitfetch',
     'PhabricatorRepositoryListController' => 'applications/repository/controller/list',
     'PhabricatorRepositoryMercurialCommitChangeParserWorker' => 'applications/repository/worker/commitchangeparser/mercurial',
     'PhabricatorRepositoryMercurialCommitDiscoveryDaemon' => 'applications/repository/daemon/commitdiscovery/mercurial',
     'PhabricatorRepositoryMercurialCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/mercurial',
     'PhabricatorRepositoryMercurialPullDaemon' => 'applications/repository/daemon/mercurialpull',
     'PhabricatorRepositoryPullLocalDaemon' => 'applications/repository/daemon/pulllocal',
     'PhabricatorRepositoryShortcut' => 'applications/repository/storage/shortcut',
     'PhabricatorRepositorySvnCommitChangeParserWorker' => 'applications/repository/worker/commitchangeparser/svn',
     'PhabricatorRepositorySvnCommitDiscoveryDaemon' => 'applications/repository/daemon/commitdiscovery/svn',
     'PhabricatorRepositorySvnCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/svn',
     'PhabricatorRepositorySymbol' => 'applications/repository/storage/symbol',
     'PhabricatorRepositoryTestCase' => 'applications/repository/storage/repository/__tests__',
     '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',
     'PhabricatorSearchScope' => 'applications/search/constants/scope',
     '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',
     'PhabricatorSymbolNameLinter' => 'infrastructure/lint/hook/xhpastsymbolname',
     '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',
     'PhabricatorTransactionView' => 'view/layout/transaction',
     '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',
     'PhabricatorUserEmailPreferenceSettingsPanelController' => 'applications/people/controller/settings/panels/emailpref',
     'PhabricatorUserEmailSettingsPanelController' => 'applications/people/controller/settings/panels/email',
     'PhabricatorUserLog' => 'applications/people/storage/log',
     'PhabricatorUserOAuthInfo' => 'applications/people/storage/useroauthinfo',
     'PhabricatorUserOAuthSettingsPanelController' => 'applications/people/controller/settings/panels/oauth',
     'PhabricatorUserPasswordSettingsPanelController' => 'applications/people/controller/settings/panels/password',
     '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',
     'PhabricatorUserTestCase' => 'applications/people/storage/user/__tests__',
     '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',
     'PhabricatorWorkerTaskUpdateController' => 'applications/daemon/controller/workertaskupdate',
     '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',
     'PhabricatorXHProfProfileView' => 'applications/xhprof/view/base',
     'PhrictionActionConstants' => 'applications/phriction/constants/action',
     'PhrictionChangeType' => 'applications/phriction/constants/changetype',
     'PhrictionConstants' => 'applications/phriction/constants/base',
     'PhrictionContent' => 'applications/phriction/storage/content',
     'PhrictionController' => 'applications/phriction/controller/base',
     'PhrictionDAO' => 'applications/phriction/storage/base',
     'PhrictionDeleteController' => 'applications/phriction/controller/delete',
     'PhrictionDiffController' => 'applications/phriction/controller/diff',
     'PhrictionDocument' => 'applications/phriction/storage/document',
     'PhrictionDocumentController' => 'applications/phriction/controller/document',
     'PhrictionDocumentEditor' => 'applications/phriction/editor/document',
     'PhrictionDocumentPreviewController' => 'applications/phriction/controller/documentpreview',
     'PhrictionDocumentStatus' => 'applications/phriction/constants/documentstatus',
     'PhrictionDocumentTestCase' => 'applications/phriction/storage/document/__tests__',
     'PhrictionEditController' => 'applications/phriction/controller/edit',
     'PhrictionHistoryController' => 'applications/phriction/controller/history',
     'PhrictionListController' => 'applications/phriction/controller/list',
     'QueryFormattingTestCase' => 'storage/qsprintf/__tests__',
   ),
   'function' =>
   array(
     '__phabricator_format_local_time' => 'view/utils',
     '_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_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',
     'Aphront403Response' => 'AphrontWebpageResponse',
     'Aphront404Response' => 'AphrontWebpageResponse',
     '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',
     'AphrontFormRadioButtonControl' => 'AphrontFormControl',
     'AphrontFormRecaptchaControl' => 'AphrontFormControl',
     'AphrontFormSelectControl' => 'AphrontFormControl',
     'AphrontFormStaticControl' => 'AphrontFormControl',
     'AphrontFormSubmitControl' => 'AphrontFormControl',
     'AphrontFormTextAreaControl' => 'AphrontFormControl',
     'AphrontFormTextControl' => 'AphrontFormControl',
     'AphrontFormToggleButtonsControl' => 'AphrontFormControl',
     'AphrontFormTokenizerControl' => 'AphrontFormControl',
     'AphrontFormView' => 'AphrontView',
     'AphrontHTTPSinkTestCase' => 'PhabricatorTestCase',
     'AphrontHeadsupActionListView' => 'AphrontView',
     'AphrontHeadsupActionView' => 'AphrontView',
     'AphrontIsolatedDatabaseConnection' => 'AphrontDatabaseConnection',
     'AphrontIsolatedDatabaseConnectionTestCase' => 'PhabricatorTestCase',
     'AphrontIsolatedHTTPSink' => 'AphrontHTTPSink',
     'AphrontJSONResponse' => 'AphrontResponse',
     'AphrontJavelinView' => 'AphrontView',
     'AphrontKeyboardShortcutsAvailableView' => 'AphrontView',
     'AphrontListFilterView' => 'AphrontView',
     'AphrontMiniPanelView' => 'AphrontView',
     'AphrontMySQLDatabaseConnection' => 'AphrontDatabaseConnection',
     'AphrontMySQLDatabaseConnectionTestCase' => 'PhabricatorTestCase',
     'AphrontNullView' => 'AphrontView',
     'AphrontPHPHTTPSink' => 'AphrontHTTPSink',
     'AphrontPageView' => 'AphrontView',
     'AphrontPagerView' => 'AphrontView',
     'AphrontPanelView' => 'AphrontView',
     'AphrontPlainTextResponse' => 'AphrontResponse',
     'AphrontQueryAccessDeniedException' => 'AphrontQueryRecoverableException',
     'AphrontQueryConnectionException' => 'AphrontQueryException',
     'AphrontQueryConnectionLostException' => 'AphrontQueryRecoverableException',
     'AphrontQueryCountException' => 'AphrontQueryException',
     'AphrontQueryDuplicateKeyException' => 'AphrontQueryException',
     'AphrontQueryObjectMissingException' => 'AphrontQueryException',
     'AphrontQueryParameterException' => 'AphrontQueryException',
     'AphrontQueryRecoverableException' => 'AphrontQueryException',
     'AphrontRedirectException' => 'AphrontException',
     'AphrontRedirectResponse' => 'AphrontResponse',
     'AphrontReloadResponse' => 'AphrontRedirectResponse',
     'AphrontRequestFailureView' => 'AphrontView',
     'AphrontRequestTestCase' => 'PhabricatorTestCase',
     'AphrontSideNavFilterView' => 'AphrontView',
     'AphrontSideNavView' => 'AphrontView',
     'AphrontTableView' => 'AphrontView',
     'AphrontTokenizerTemplateView' => 'AphrontView',
     'AphrontTypeaheadTemplateView' => 'AphrontView',
     'AphrontWebpageResponse' => 'AphrontResponse',
     'CelerityResourceController' => 'AphrontController',
     'CelerityResourceGraph' => 'AbstractDirectedGraph',
     'ConduitAPI_arcanist_Method' => 'ConduitAPIMethod',
     'ConduitAPI_arcanist_projectinfo_Method' => 'ConduitAPI_arcanist_Method',
     'ConduitAPI_chatlog_Method' => 'ConduitAPIMethod',
     'ConduitAPI_chatlog_query_Method' => 'ConduitAPI_chatlog_Method',
     'ConduitAPI_chatlog_record_Method' => 'ConduitAPI_chatlog_Method',
     '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_createcomment_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_getrevisioncomments_Method' => 'ConduitAPIMethod',
     'ConduitAPI_differential_getrevisionfeedback_Method' => 'ConduitAPIMethod',
     'ConduitAPI_differential_markcommitted_Method' => 'ConduitAPIMethod',
     'ConduitAPI_differential_parsecommitmessage_Method' => 'ConduitAPIMethod',
     'ConduitAPI_differential_query_Method' => 'ConduitAPIMethod',
     'ConduitAPI_differential_setdiffproperty_Method' => 'ConduitAPIMethod',
     'ConduitAPI_differential_updaterevision_Method' => 'ConduitAPIMethod',
     'ConduitAPI_differential_updatetaskrevisionassoc_Method' => 'ConduitAPIMethod',
     'ConduitAPI_differential_updateunitresults_Method' => 'ConduitAPIMethod',
     'ConduitAPI_diffusion_findsymbols_Method' => 'ConduitAPIMethod',
     'ConduitAPI_diffusion_getcommits_Method' => 'ConduitAPIMethod',
     'ConduitAPI_diffusion_getrecentcommitsbypath_Method' => 'ConduitAPIMethod',
     'ConduitAPI_feed_publish_Method' => 'ConduitAPIMethod',
     'ConduitAPI_feed_query_Method' => 'ConduitAPIMethod',
     'ConduitAPI_file_download_Method' => 'ConduitAPIMethod',
     'ConduitAPI_file_info_Method' => 'ConduitAPIMethod',
     'ConduitAPI_file_upload_Method' => 'ConduitAPIMethod',
     'ConduitAPI_maniphest_Method' => 'ConduitAPIMethod',
     'ConduitAPI_maniphest_createtask_Method' => 'ConduitAPI_maniphest_Method',
     'ConduitAPI_maniphest_find_Method' => 'ConduitAPI_maniphest_Method',
     'ConduitAPI_maniphest_gettasktransactions_Method' => 'ConduitAPI_maniphest_Method',
     'ConduitAPI_maniphest_info_Method' => 'ConduitAPI_maniphest_Method',
     'ConduitAPI_maniphest_update_Method' => 'ConduitAPI_maniphest_Method',
     'ConduitAPI_paste_Method' => 'ConduitAPIMethod',
     'ConduitAPI_paste_create_Method' => 'ConduitAPI_paste_Method',
     'ConduitAPI_paste_info_Method' => 'ConduitAPI_paste_Method',
     'ConduitAPI_path_getowners_Method' => 'ConduitAPIMethod',
     'ConduitAPI_phid_Method' => 'ConduitAPIMethod',
     'ConduitAPI_phid_info_Method' => 'ConduitAPI_phid_Method',
     'ConduitAPI_phid_query_Method' => 'ConduitAPI_phid_Method',
     'ConduitAPI_phriction_Method' => 'ConduitAPIMethod',
     'ConduitAPI_phriction_edit_Method' => 'ConduitAPI_phriction_Method',
     'ConduitAPI_phriction_history_Method' => 'ConduitAPI_phriction_Method',
     'ConduitAPI_phriction_info_Method' => 'ConduitAPI_phriction_Method',
     'ConduitAPI_project_Method' => 'ConduitAPIMethod',
     'ConduitAPI_project_query_Method' => 'ConduitAPI_project_Method',
     'ConduitAPI_remarkup_process_Method' => 'ConduitAPIMethod',
     'ConduitAPI_slowvote_info_Method' => 'ConduitAPIMethod',
     'ConduitAPI_user_Method' => 'ConduitAPIMethod',
     'ConduitAPI_user_find_Method' => 'ConduitAPI_user_Method',
     'ConduitAPI_user_info_Method' => 'ConduitAPI_user_Method',
     'ConduitAPI_user_whoami_Method' => 'ConduitAPI_user_Method',
     'DarkConsoleConfigPlugin' => 'DarkConsolePlugin',
     'DarkConsoleController' => 'PhabricatorController',
     'DarkConsoleErrorLogPlugin' => 'DarkConsolePlugin',
     'DarkConsoleEventPlugin' => 'DarkConsolePlugin',
     'DarkConsoleEventPluginAPI' => 'PhutilEventListener',
     'DarkConsoleRequestPlugin' => 'DarkConsolePlugin',
     'DarkConsoleServicesPlugin' => 'DarkConsolePlugin',
     'DarkConsoleXHProfPlugin' => 'DarkConsolePlugin',
     'DifferentialActionHasNoEffectException' => 'DifferentialException',
     'DifferentialAddCommentView' => 'AphrontView',
     'DifferentialAffectedPath' => 'DifferentialDAO',
     'DifferentialApplyPatchFieldSpecification' => 'DifferentialFieldSpecification',
     'DifferentialArcanistProjectFieldSpecification' => 'DifferentialFieldSpecification',
     'DifferentialAuthorFieldSpecification' => 'DifferentialFieldSpecification',
     'DifferentialAuxiliaryField' => 'DifferentialDAO',
     'DifferentialBlameRevisionFieldSpecification' => 'DifferentialFieldSpecification',
     'DifferentialBranchFieldSpecification' => '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',
     'DifferentialDateCreatedFieldSpecification' => 'DifferentialFieldSpecification',
     'DifferentialDateModifiedFieldSpecification' => 'DifferentialFieldSpecification',
     'DifferentialDefaultFieldSelector' => 'DifferentialFieldSelector',
     'DifferentialDependenciesFieldSpecification' => 'DifferentialFieldSpecification',
     'DifferentialDiff' => 'DifferentialDAO',
     'DifferentialDiffContentMail' => 'DifferentialMail',
     'DifferentialDiffCreateController' => 'DifferentialController',
     'DifferentialDiffProperty' => 'DifferentialDAO',
     'DifferentialDiffTableOfContentsView' => 'AphrontView',
     'DifferentialDiffViewController' => 'DifferentialController',
     'DifferentialExceptionMail' => 'DifferentialMail',
     'DifferentialExportPatchFieldSpecification' => 'DifferentialFieldSpecification',
     'DifferentialGitSVNIDFieldSpecification' => 'DifferentialFieldSpecification',
     'DifferentialHostFieldSpecification' => 'DifferentialFieldSpecification',
     'DifferentialHunk' => 'DifferentialDAO',
     'DifferentialInlineComment' => 'DifferentialDAO',
     'DifferentialInlineCommentEditController' => 'DifferentialController',
     'DifferentialInlineCommentEditView' => 'AphrontView',
     'DifferentialInlineCommentPreviewController' => 'DifferentialController',
     'DifferentialInlineCommentView' => 'AphrontView',
     'DifferentialLinesFieldSpecification' => 'DifferentialFieldSpecification',
     'DifferentialLintFieldSpecification' => 'DifferentialFieldSpecification',
     'DifferentialLocalCommitsView' => 'AphrontView',
     'DifferentialManiphestTasksFieldSpecification' => 'DifferentialFieldSpecification',
     'DifferentialNewDiffMail' => 'DifferentialReviewRequestMail',
     'DifferentialPathFieldSpecification' => 'DifferentialFieldSpecification',
     'DifferentialPrimaryPaneView' => 'AphrontView',
     'DifferentialReplyHandler' => 'PhabricatorMailReplyHandler',
     'DifferentialRevertPlanFieldSpecification' => 'DifferentialFieldSpecification',
     'DifferentialReviewRequestMail' => 'DifferentialMail',
     'DifferentialReviewedByFieldSpecification' => 'DifferentialFieldSpecification',
     'DifferentialReviewersFieldSpecification' => 'DifferentialFieldSpecification',
     'DifferentialRevision' => 'DifferentialDAO',
     'DifferentialRevisionCommentListView' => 'AphrontView',
     'DifferentialRevisionCommentView' => 'AphrontView',
     'DifferentialRevisionDetailView' => 'AphrontView',
     'DifferentialRevisionEditController' => 'DifferentialController',
     'DifferentialRevisionIDFieldParserTestCase' => 'PhabricatorTestCase',
     'DifferentialRevisionIDFieldSpecification' => 'DifferentialFieldSpecification',
     'DifferentialRevisionListController' => 'DifferentialController',
     'DifferentialRevisionListView' => 'AphrontView',
     'DifferentialRevisionStatsController' => 'DifferentialController',
     'DifferentialRevisionStatsView' => 'AphrontView',
     'DifferentialRevisionStatusFieldSpecification' => 'DifferentialFieldSpecification',
     'DifferentialRevisionUpdateHistoryView' => 'AphrontView',
     'DifferentialRevisionViewController' => 'DifferentialController',
     'DifferentialSubscribeController' => 'DifferentialController',
     'DifferentialSummaryFieldSpecification' => 'DifferentialFieldSpecification',
     'DifferentialTestPlanFieldSpecification' => 'DifferentialFieldSpecification',
     'DifferentialTitleFieldSpecification' => 'DifferentialFieldSpecification',
     'DifferentialUnitFieldSpecification' => 'DifferentialFieldSpecification',
     'DiffusionBranchTableView' => 'DiffusionView',
     'DiffusionBrowseController' => 'DiffusionController',
     'DiffusionBrowseFileController' => 'DiffusionController',
     'DiffusionBrowseTableView' => 'DiffusionView',
     'DiffusionChangeController' => 'DiffusionController',
     'DiffusionCommentListView' => 'AphrontView',
     'DiffusionCommentView' => 'AphrontView',
     'DiffusionCommitChangeTableView' => 'DiffusionView',
     'DiffusionCommitController' => 'DiffusionController',
     'DiffusionContainsQuery' => 'DiffusionQuery',
     'DiffusionController' => 'PhabricatorController',
     'DiffusionDiffController' => 'DiffusionController',
     'DiffusionEmptyResultView' => 'DiffusionView',
     'DiffusionGitBranchQuery' => 'DiffusionBranchQuery',
     'DiffusionGitBranchQueryTestCase' => 'PhabricatorTestCase',
     'DiffusionGitBrowseQuery' => 'DiffusionBrowseQuery',
     'DiffusionGitContainsQuery' => 'DiffusionContainsQuery',
     'DiffusionGitDiffQuery' => 'DiffusionDiffQuery',
     'DiffusionGitFileContentQuery' => 'DiffusionFileContentQuery',
     'DiffusionGitHistoryQuery' => 'DiffusionHistoryQuery',
     'DiffusionGitLastModifiedQuery' => 'DiffusionLastModifiedQuery',
     'DiffusionGitRequest' => 'DiffusionRequest',
     'DiffusionHistoryController' => 'DiffusionController',
     'DiffusionHistoryTableView' => 'DiffusionView',
     'DiffusionHomeController' => 'DiffusionController',
     'DiffusionLastModifiedController' => 'DiffusionController',
     'DiffusionMercurialBranchQuery' => 'DiffusionBranchQuery',
     'DiffusionMercurialBrowseQuery' => 'DiffusionBrowseQuery',
     'DiffusionMercurialContainsQuery' => 'DiffusionContainsQuery',
     'DiffusionMercurialDiffQuery' => 'DiffusionDiffQuery',
     'DiffusionMercurialFileContentQuery' => 'DiffusionFileContentQuery',
     'DiffusionMercurialHistoryQuery' => 'DiffusionHistoryQuery',
     'DiffusionMercurialLastModifiedQuery' => 'DiffusionLastModifiedQuery',
     'DiffusionMercurialRequest' => 'DiffusionRequest',
     'DiffusionPathCompleteController' => 'DiffusionController',
     'DiffusionPathQueryTestCase' => 'PhabricatorTestCase',
     'DiffusionPathValidateController' => 'DiffusionController',
     'DiffusionRepositoryController' => 'DiffusionController',
     'DiffusionSVNContainsQuery' => 'DiffusionContainsQuery',
     'DiffusionSvnBrowseQuery' => 'DiffusionBrowseQuery',
     'DiffusionSvnDiffQuery' => 'DiffusionDiffQuery',
     'DiffusionSvnFileContentQuery' => 'DiffusionFileContentQuery',
     'DiffusionSvnHistoryQuery' => 'DiffusionHistoryQuery',
     'DiffusionSvnLastModifiedQuery' => 'DiffusionLastModifiedQuery',
     'DiffusionSvnRequest' => 'DiffusionRequest',
     'DiffusionSymbolController' => 'DiffusionController',
     'DiffusionView' => 'AphrontView',
     'DrydockAllocatorWorker' => 'PhabricatorWorker',
     'DrydockCommandInterface' => 'DrydockInterface',
     'DrydockController' => 'PhabricatorController',
     'DrydockDAO' => 'PhabricatorLiskDAO',
     'DrydockEC2HostBlueprint' => 'DrydockRemoteHostBlueprint',
     'DrydockLease' => 'DrydockDAO',
     'DrydockLeaseListController' => 'DrydockController',
     'DrydockLeaseStatus' => 'DrydockConstants',
     'DrydockLocalCommandInterface' => 'DrydockCommandInterface',
     'DrydockLocalHostBlueprint' => 'DrydockBlueprint',
     'DrydockRemoteHostBlueprint' => 'DrydockBlueprint',
     'DrydockResource' => 'DrydockDAO',
     'DrydockResourceAllocateController' => 'DrydockController',
     'DrydockResourceListController' => 'DrydockController',
     'DrydockResourceStatus' => 'DrydockConstants',
     'DrydockSSHCommandInterface' => 'DrydockCommandInterface',
     'HeraldAction' => 'HeraldDAO',
     'HeraldAllRulesController' => 'HeraldController',
     'HeraldApplyTranscript' => 'HeraldDAO',
     'HeraldCommitAdapter' => 'HeraldObjectAdapter',
     'HeraldCondition' => 'HeraldDAO',
     'HeraldController' => 'PhabricatorController',
     'HeraldDAO' => 'PhabricatorLiskDAO',
     'HeraldDeleteController' => 'HeraldController',
     'HeraldDifferentialRevisionAdapter' => 'HeraldObjectAdapter',
     'HeraldDryRunAdapter' => 'HeraldObjectAdapter',
     'HeraldHomeController' => 'HeraldController',
     'HeraldNewController' => 'HeraldController',
     'HeraldRule' => 'HeraldDAO',
     'HeraldRuleController' => 'HeraldController',
     'HeraldRuleEdit' => 'HeraldDAO',
     'HeraldRuleEditHistoryController' => 'HeraldController',
     'HeraldRuleEditHistoryView' => 'AphrontView',
     'HeraldRuleListView' => 'AphrontView',
     'HeraldTestConsoleController' => 'HeraldController',
     'HeraldTranscript' => 'HeraldDAO',
     'HeraldTranscriptController' => 'HeraldController',
     'HeraldTranscriptListController' => 'HeraldController',
     'JavelinReactorExample' => 'PhabricatorUIExample',
     'JavelinViewExample' => 'PhabricatorUIExample',
     'JavelinViewExampleServerView' => 'AphrontView',
     'LiskIsolationTestCase' => 'PhabricatorTestCase',
     'LiskIsolationTestDAO' => 'LiskDAO',
     'ManiphestAction' => 'PhrictionConstants',
     'ManiphestAuxiliaryFieldDefaultSpecification' => 'ManiphestAuxiliaryFieldSpecification',
     'ManiphestBatchEditController' => 'ManiphestController',
     'ManiphestController' => 'PhabricatorController',
     'ManiphestDAO' => 'PhabricatorLiskDAO',
     'ManiphestDefaultTaskExtensions' => 'ManiphestTaskExtensions',
     'ManiphestExportController' => 'ManiphestController',
     'ManiphestReplyHandler' => 'PhabricatorMailReplyHandler',
     'ManiphestReportController' => 'ManiphestController',
     'ManiphestTask' => 'ManiphestDAO',
     'ManiphestTaskAuxiliaryStorage' => 'ManiphestDAO',
     'ManiphestTaskDescriptionChangeController' => 'ManiphestController',
     'ManiphestTaskDescriptionDiffController' => 'ManiphestTaskDescriptionChangeController',
     'ManiphestTaskDescriptionPreviewController' => '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',
     'MetaMTANotificationType' => 'MetaMTAConstants',
     'Phabricator404Controller' => 'PhabricatorController',
     'PhabricatorAuditAddCommentController' => 'PhabricatorAuditController',
     'PhabricatorAuditComment' => 'PhabricatorAuditDAO',
     'PhabricatorAuditCommitListView' => 'AphrontView',
     'PhabricatorAuditController' => 'PhabricatorController',
     'PhabricatorAuditDAO' => 'PhabricatorLiskDAO',
     'PhabricatorAuditListController' => 'PhabricatorAuditController',
     'PhabricatorAuditListView' => 'AphrontView',
     'PhabricatorAuditPreviewController' => 'PhabricatorAuditController',
     'PhabricatorAuditReplyHandler' => 'PhabricatorMailReplyHandler',
     'PhabricatorAuthController' => 'PhabricatorController',
     'PhabricatorCalendarBrowseController' => 'PhabricatorCalendarController',
     'PhabricatorCalendarController' => 'PhabricatorController',
     'PhabricatorChatLogChannelListController' => 'PhabricatorChatLogController',
     'PhabricatorChatLogChannelLogController' => 'PhabricatorChatLogController',
     'PhabricatorChatLogController' => 'PhabricatorController',
     'PhabricatorChatLogDAO' => 'PhabricatorLiskDAO',
     'PhabricatorChatLogEvent' => 'PhabricatorChatLogDAO',
     'PhabricatorChatLogEventType' => 'PhabricatorChatLogConstants',
     'PhabricatorConduitAPIController' => 'PhabricatorConduitController',
     'PhabricatorConduitCertificateToken' => 'PhabricatorConduitDAO',
     'PhabricatorConduitConnectionLog' => 'PhabricatorConduitDAO',
     'PhabricatorConduitConsoleController' => 'PhabricatorConduitController',
     'PhabricatorConduitController' => 'PhabricatorController',
     'PhabricatorConduitDAO' => 'PhabricatorLiskDAO',
     'PhabricatorConduitLogController' => 'PhabricatorConduitController',
     'PhabricatorConduitMethodCallLog' => 'PhabricatorConduitDAO',
     'PhabricatorConduitTokenController' => 'PhabricatorConduitController',
     'PhabricatorContentSourceView' => 'AphrontView',
     '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',
     'PhabricatorDirectoryCategoryViewController' => 'PhabricatorDirectoryController',
     'PhabricatorDirectoryController' => 'PhabricatorController',
     'PhabricatorDirectoryDAO' => 'PhabricatorLiskDAO',
     'PhabricatorDirectoryEditController' => 'PhabricatorDirectoryController',
     'PhabricatorDirectoryItem' => 'PhabricatorDirectoryDAO',
     'PhabricatorDirectoryItemDeleteController' => 'PhabricatorDirectoryController',
     'PhabricatorDirectoryItemEditController' => 'PhabricatorDirectoryController',
     'PhabricatorDirectoryMainController' => 'PhabricatorDirectoryController',
     'PhabricatorDisabledUserController' => 'PhabricatorAuthController',
     'PhabricatorDraft' => 'PhabricatorDraftDAO',
     'PhabricatorDraftDAO' => 'PhabricatorLiskDAO',
     'PhabricatorEmailLoginController' => 'PhabricatorAuthController',
     'PhabricatorEmailTokenController' => 'PhabricatorAuthController',
     'PhabricatorEnvTestCase' => 'PhabricatorTestCase',
     'PhabricatorEvent' => 'PhutilEvent',
     'PhabricatorEventType' => 'PhutilEventType',
     'PhabricatorFeedController' => 'PhabricatorController',
     'PhabricatorFeedDAO' => 'PhabricatorLiskDAO',
     'PhabricatorFeedPublicStreamController' => 'PhabricatorFeedController',
     'PhabricatorFeedStoryAudit' => 'PhabricatorFeedStory',
     'PhabricatorFeedStoryData' => 'PhabricatorFeedDAO',
     'PhabricatorFeedStoryDifferential' => 'PhabricatorFeedStory',
     'PhabricatorFeedStoryManiphest' => 'PhabricatorFeedStory',
     'PhabricatorFeedStoryPhriction' => 'PhabricatorFeedStory',
     'PhabricatorFeedStoryProject' => 'PhabricatorFeedStory',
     'PhabricatorFeedStoryReference' => 'PhabricatorFeedDAO',
     'PhabricatorFeedStoryStatus' => 'PhabricatorFeedStory',
     'PhabricatorFeedStoryTypeConstants' => 'PhabricatorFeedConstants',
     'PhabricatorFeedStoryUnknown' => 'PhabricatorFeedStory',
     'PhabricatorFeedStoryView' => 'PhabricatorFeedView',
     'PhabricatorFeedView' => 'AphrontView',
     'PhabricatorFile' => 'PhabricatorFileDAO',
     'PhabricatorFileController' => 'PhabricatorController',
     'PhabricatorFileDAO' => 'PhabricatorLiskDAO',
     'PhabricatorFileDataController' => 'PhabricatorFileController',
     'PhabricatorFileDeleteController' => 'PhabricatorFileController',
     'PhabricatorFileDropUploadController' => 'PhabricatorFileController',
     'PhabricatorFileImageMacro' => 'PhabricatorFileDAO',
     'PhabricatorFileInfoController' => 'PhabricatorFileController',
     'PhabricatorFileListController' => 'PhabricatorFileController',
     'PhabricatorFileMacroDeleteController' => 'PhabricatorFileController',
     'PhabricatorFileMacroEditController' => 'PhabricatorFileController',
     'PhabricatorFileMacroListController' => 'PhabricatorFileController',
     'PhabricatorFileProxyController' => 'PhabricatorFileController',
     'PhabricatorFileProxyImage' => 'PhabricatorFileDAO',
     'PhabricatorFileSideNavView' => 'AphrontView',
     'PhabricatorFileStorageBlob' => 'PhabricatorFileDAO',
     'PhabricatorFileTransformController' => 'PhabricatorFileController',
     'PhabricatorFileUploadController' => 'PhabricatorFileController',
     'PhabricatorFileUploadView' => 'AphrontView',
     'PhabricatorGarbageCollectorDaemon' => 'PhabricatorDaemon',
     'PhabricatorGoodForNothingWorker' => 'PhabricatorWorker',
     'PhabricatorHelpController' => 'PhabricatorController',
     'PhabricatorHelpKeyboardShortcutController' => 'PhabricatorHelpController',
     'PhabricatorIRCBot' => 'PhabricatorDaemon',
     'PhabricatorIRCDifferentialNotificationHandler' => 'PhabricatorIRCHandler',
     'PhabricatorIRCLogHandler' => 'PhabricatorIRCHandler',
     'PhabricatorIRCObjectNameHandler' => 'PhabricatorIRCHandler',
     'PhabricatorIRCProtocolHandler' => 'PhabricatorIRCHandler',
     'PhabricatorIRCWhatsNewHandler' => 'PhabricatorIRCHandler',
     'PhabricatorInfrastructureTestCase' => 'PhabricatorTestCase',
     'PhabricatorJavelinLinter' => 'ArcanistLinter',
     'PhabricatorLintEngine' => 'PhutilLintEngine',
     'PhabricatorLiskDAO' => 'LiskDAO',
     'PhabricatorLocalDiskFileStorageEngine' => 'PhabricatorFileStorageEngine',
     'PhabricatorLocalTimeTestCase' => 'PhabricatorTestCase',
     'PhabricatorLoginController' => 'PhabricatorAuthController',
     'PhabricatorLoginValidateController' => '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',
     'PhabricatorOAuthClientAuthorization' => 'PhabricatorOAuthServerDAO',
     'PhabricatorOAuthClientAuthorizationBaseController' => 'PhabricatorOAuthServerController',
     'PhabricatorOAuthClientAuthorizationDeleteController' => 'PhabricatorOAuthClientAuthorizationBaseController',
     'PhabricatorOAuthClientAuthorizationEditController' => 'PhabricatorOAuthClientAuthorizationBaseController',
     'PhabricatorOAuthClientAuthorizationListController' => 'PhabricatorOAuthClientAuthorizationBaseController',
     'PhabricatorOAuthClientBaseController' => 'PhabricatorOAuthServerController',
     'PhabricatorOAuthClientDeleteController' => 'PhabricatorOAuthClientBaseController',
     'PhabricatorOAuthClientEditController' => 'PhabricatorOAuthClientBaseController',
     'PhabricatorOAuthClientListController' => 'PhabricatorOAuthClientBaseController',
     'PhabricatorOAuthClientViewController' => 'PhabricatorOAuthClientBaseController',
     'PhabricatorOAuthDefaultRegistrationController' => 'PhabricatorOAuthRegistrationController',
     'PhabricatorOAuthDiagnosticsController' => 'PhabricatorAuthController',
     'PhabricatorOAuthFailureView' => 'AphrontView',
     'PhabricatorOAuthLoginController' => 'PhabricatorAuthController',
     'PhabricatorOAuthProviderFacebook' => 'PhabricatorOAuthProvider',
     'PhabricatorOAuthProviderGitHub' => 'PhabricatorOAuthProvider',
     'PhabricatorOAuthProviderGoogle' => 'PhabricatorOAuthProvider',
     'PhabricatorOAuthProviderPhabricator' => 'PhabricatorOAuthProvider',
     'PhabricatorOAuthRegistrationController' => 'PhabricatorAuthController',
     'PhabricatorOAuthResponse' => 'AphrontResponse',
     'PhabricatorOAuthServerAccessToken' => 'PhabricatorOAuthServerDAO',
     'PhabricatorOAuthServerAuthController' => 'PhabricatorAuthController',
     'PhabricatorOAuthServerAuthorizationCode' => 'PhabricatorOAuthServerDAO',
     'PhabricatorOAuthServerClient' => 'PhabricatorOAuthServerDAO',
     'PhabricatorOAuthServerController' => 'PhabricatorController',
     'PhabricatorOAuthServerDAO' => 'PhabricatorLiskDAO',
+    'PhabricatorOAuthServerTestCase' => 'PhabricatorTestCase',
     'PhabricatorOAuthServerTestController' => 'PhabricatorOAuthServerController',
     'PhabricatorOAuthServerTokenController' => 'PhabricatorAuthController',
     'PhabricatorOAuthUnlinkController' => 'PhabricatorAuthController',
     'PhabricatorObjectGraph' => 'AbstractDirectedGraph',
     'PhabricatorObjectHandleStatus' => 'PhabricatorObjectHandleConstants',
     'PhabricatorOwnerRelatedListController' => 'PhabricatorOwnersController',
     'PhabricatorOwnersController' => 'PhabricatorController',
     'PhabricatorOwnersDAO' => 'PhabricatorLiskDAO',
     'PhabricatorOwnersDeleteController' => 'PhabricatorOwnersController',
     'PhabricatorOwnersDetailController' => 'PhabricatorOwnersController',
     'PhabricatorOwnersEditController' => 'PhabricatorOwnersController',
     'PhabricatorOwnersListController' => 'PhabricatorOwnersController',
     'PhabricatorOwnersOwner' => 'PhabricatorOwnersDAO',
     'PhabricatorOwnersPackage' => 'PhabricatorOwnersDAO',
     'PhabricatorOwnersPackageCommitRelationship' => 'PhabricatorOwnersDAO',
     'PhabricatorOwnersPath' => 'PhabricatorOwnersDAO',
     'PhabricatorPHID' => 'PhabricatorPHIDDAO',
     'PhabricatorPHIDController' => 'PhabricatorController',
     'PhabricatorPHIDDAO' => 'PhabricatorLiskDAO',
     'PhabricatorPHIDLookupController' => 'PhabricatorPHIDController',
     'PhabricatorPaste' => 'PhabricatorPasteDAO',
     'PhabricatorPasteController' => 'PhabricatorController',
     'PhabricatorPasteDAO' => 'PhabricatorLiskDAO',
     'PhabricatorPasteListController' => 'PhabricatorPasteController',
     'PhabricatorPasteViewController' => 'PhabricatorPasteController',
     'PhabricatorPeopleController' => 'PhabricatorController',
     'PhabricatorPeopleEditController' => 'PhabricatorPeopleController',
     'PhabricatorPeopleListController' => 'PhabricatorPeopleController',
     'PhabricatorPeopleLogsController' => 'PhabricatorPeopleController',
     'PhabricatorPeopleProfileController' => 'PhabricatorPeopleController',
     'PhabricatorProfileHeaderView' => 'AphrontView',
     'PhabricatorProject' => 'PhabricatorProjectDAO',
     'PhabricatorProjectAffiliation' => 'PhabricatorProjectDAO',
     'PhabricatorProjectController' => 'PhabricatorController',
     'PhabricatorProjectCreateController' => 'PhabricatorProjectController',
     'PhabricatorProjectDAO' => 'PhabricatorLiskDAO',
     'PhabricatorProjectListController' => 'PhabricatorProjectController',
     'PhabricatorProjectProfile' => 'PhabricatorProjectDAO',
     'PhabricatorProjectProfileController' => 'PhabricatorProjectController',
     'PhabricatorProjectProfileEditController' => 'PhabricatorProjectController',
     'PhabricatorProjectSubproject' => 'PhabricatorProjectDAO',
     'PhabricatorProjectTransaction' => 'PhabricatorProjectDAO',
     'PhabricatorProjectTransactionType' => 'PhabricatorProjectConstants',
     'PhabricatorProjectUpdateController' => 'PhabricatorProjectController',
     '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',
     'PhabricatorRepositoryCommitOwnersWorker' => 'PhabricatorRepositoryCommitParserWorker',
     'PhabricatorRepositoryCommitParserWorker' => 'PhabricatorWorker',
     'PhabricatorRepositoryCommitTaskDaemon' => 'PhabricatorRepositoryDaemon',
     'PhabricatorRepositoryController' => 'PhabricatorController',
     'PhabricatorRepositoryCreateController' => 'PhabricatorRepositoryController',
     'PhabricatorRepositoryDAO' => 'PhabricatorLiskDAO',
     'PhabricatorRepositoryDaemon' => 'PhabricatorDaemon',
     'PhabricatorRepositoryDefaultCommitMessageDetailParser' => 'PhabricatorRepositoryCommitMessageDetailParser',
     'PhabricatorRepositoryDeleteController' => 'PhabricatorRepositoryController',
     'PhabricatorRepositoryEditController' => 'PhabricatorRepositoryController',
     'PhabricatorRepositoryGitCommitChangeParserWorker' => 'PhabricatorRepositoryCommitChangeParserWorker',
     'PhabricatorRepositoryGitCommitDiscoveryDaemon' => 'PhabricatorRepositoryCommitDiscoveryDaemon',
     'PhabricatorRepositoryGitCommitDiscoveryDaemonTestCase' => 'PhabricatorTestCase',
     'PhabricatorRepositoryGitCommitMessageParserWorker' => 'PhabricatorRepositoryCommitMessageParserWorker',
     'PhabricatorRepositoryGitFetchDaemon' => 'PhabricatorRepositoryPullLocalDaemon',
     'PhabricatorRepositoryListController' => 'PhabricatorRepositoryController',
     'PhabricatorRepositoryMercurialCommitChangeParserWorker' => 'PhabricatorRepositoryCommitChangeParserWorker',
     'PhabricatorRepositoryMercurialCommitDiscoveryDaemon' => 'PhabricatorRepositoryCommitDiscoveryDaemon',
     'PhabricatorRepositoryMercurialCommitMessageParserWorker' => 'PhabricatorRepositoryCommitMessageParserWorker',
     'PhabricatorRepositoryMercurialPullDaemon' => 'PhabricatorRepositoryPullLocalDaemon',
     'PhabricatorRepositoryPullLocalDaemon' => 'PhabricatorRepositoryDaemon',
     'PhabricatorRepositoryShortcut' => 'PhabricatorRepositoryDAO',
     'PhabricatorRepositorySvnCommitChangeParserWorker' => 'PhabricatorRepositoryCommitChangeParserWorker',
     'PhabricatorRepositorySvnCommitDiscoveryDaemon' => 'PhabricatorRepositoryCommitDiscoveryDaemon',
     'PhabricatorRepositorySvnCommitMessageParserWorker' => 'PhabricatorRepositoryCommitMessageParserWorker',
     'PhabricatorRepositorySymbol' => 'PhabricatorRepositoryDAO',
     'PhabricatorRepositoryTestCase' => 'PhabricatorTestCase',
     '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',
     'PhabricatorSymbolNameLinter' => 'ArcanistXHPASTLintNamingHook',
     'PhabricatorTaskmasterDaemon' => 'PhabricatorDaemon',
     'PhabricatorTestCase' => 'ArcanistPhutilTestCase',
     'PhabricatorTimelineCursor' => 'PhabricatorTimelineDAO',
     'PhabricatorTimelineDAO' => 'PhabricatorLiskDAO',
     'PhabricatorTimelineEvent' => 'PhabricatorTimelineDAO',
     'PhabricatorTimelineEventData' => 'PhabricatorTimelineDAO',
     'PhabricatorTimer' => 'PhabricatorCountdownDAO',
     'PhabricatorTransactionView' => 'AphrontView',
     'PhabricatorTransformedFile' => 'PhabricatorFileDAO',
     'PhabricatorTrivialTestCase' => 'PhabricatorTestCase',
     'PhabricatorTypeaheadCommonDatasourceController' => 'PhabricatorTypeaheadDatasourceController',
     'PhabricatorTypeaheadDatasourceController' => 'PhabricatorController',
     'PhabricatorUIExampleController' => 'PhabricatorController',
     'PhabricatorUIExampleRenderController' => 'PhabricatorUIExampleController',
     'PhabricatorUIListFilterExample' => 'PhabricatorUIExample',
     'PhabricatorUIPagerExample' => 'PhabricatorUIExample',
     'PhabricatorUser' => 'PhabricatorUserDAO',
     'PhabricatorUserAccountSettingsPanelController' => 'PhabricatorUserSettingsPanelController',
     'PhabricatorUserConduitSettingsPanelController' => 'PhabricatorUserSettingsPanelController',
     'PhabricatorUserDAO' => 'PhabricatorLiskDAO',
     'PhabricatorUserEmailPreferenceSettingsPanelController' => 'PhabricatorUserSettingsPanelController',
     'PhabricatorUserEmailSettingsPanelController' => 'PhabricatorUserSettingsPanelController',
     'PhabricatorUserLog' => 'PhabricatorUserDAO',
     'PhabricatorUserOAuthInfo' => 'PhabricatorUserDAO',
     'PhabricatorUserOAuthSettingsPanelController' => 'PhabricatorUserSettingsPanelController',
     'PhabricatorUserPasswordSettingsPanelController' => 'PhabricatorUserSettingsPanelController',
     'PhabricatorUserPreferenceSettingsPanelController' => 'PhabricatorUserSettingsPanelController',
     'PhabricatorUserPreferences' => 'PhabricatorUserDAO',
     'PhabricatorUserProfile' => 'PhabricatorUserDAO',
     'PhabricatorUserProfileSettingsPanelController' => 'PhabricatorUserSettingsPanelController',
     'PhabricatorUserSSHKey' => 'PhabricatorUserDAO',
     'PhabricatorUserSSHKeysSettingsPanelController' => 'PhabricatorUserSettingsPanelController',
     'PhabricatorUserSettingsController' => 'PhabricatorPeopleController',
     'PhabricatorUserSettingsPanelController' => 'PhabricatorPeopleController',
     'PhabricatorUserTestCase' => 'PhabricatorTestCase',
     'PhabricatorWorkerDAO' => 'PhabricatorLiskDAO',
     'PhabricatorWorkerTask' => 'PhabricatorWorkerDAO',
     'PhabricatorWorkerTaskData' => 'PhabricatorWorkerDAO',
     'PhabricatorWorkerTaskDetailController' => 'PhabricatorDaemonController',
     'PhabricatorWorkerTaskUpdateController' => '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' => 'PhabricatorXHProfProfileView',
     'PhabricatorXHProfProfileTopLevelView' => 'PhabricatorXHProfProfileView',
     'PhabricatorXHProfProfileView' => 'AphrontView',
     'PhrictionActionConstants' => 'PhrictionConstants',
     'PhrictionChangeType' => 'PhrictionConstants',
     'PhrictionContent' => 'PhrictionDAO',
     'PhrictionController' => 'PhabricatorController',
     'PhrictionDAO' => 'PhabricatorLiskDAO',
     'PhrictionDeleteController' => 'PhrictionController',
     'PhrictionDiffController' => 'PhrictionController',
     'PhrictionDocument' => 'PhrictionDAO',
     'PhrictionDocumentController' => 'PhrictionController',
     'PhrictionDocumentPreviewController' => 'PhrictionController',
     'PhrictionDocumentStatus' => 'PhrictionConstants',
     'PhrictionDocumentTestCase' => 'PhabricatorTestCase',
     'PhrictionEditController' => 'PhrictionController',
     'PhrictionHistoryController' => 'PhrictionController',
     'PhrictionListController' => 'PhrictionController',
     'QueryFormattingTestCase' => 'PhabricatorTestCase',
   ),
   'requires_interface' =>
   array(
   ),
 ));
diff --git a/src/aphront/response/base/AphrontResponse.php b/src/aphront/response/base/AphrontResponse.php
index f3bdc74674..437fb9df85 100644
--- a/src/aphront/response/base/AphrontResponse.php
+++ b/src/aphront/response/base/AphrontResponse.php
@@ -1,144 +1,147 @@
 <?php
 
 /*
  * Copyright 2012 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.
  */
 
 /**
  * @group aphront
  */
 abstract class AphrontResponse {
 
   private $request;
   private $cacheable = false;
   private $responseCode = 200;
   private $lastModified = null;
 
   protected $frameable;
 
   public function setRequest($request) {
     $this->request = $request;
     return $this;
   }
 
   public function getRequest() {
     return $this->request;
   }
 
   public function getHeaders() {
     $headers = array();
     if (!$this->frameable) {
       $headers[] = array('X-Frame-Options', 'Deny');
     }
 
     return $headers;
   }
 
   public function setCacheDurationInSeconds($duration) {
     $this->cacheable = $duration;
     return $this;
   }
 
   public function setLastModified($epoch_timestamp) {
     $this->lastModified = $epoch_timestamp;
     return $this;
   }
 
   public function setHTTPResponseCode($code) {
     $this->responseCode = $code;
     return $this;
   }
 
   public function getHTTPResponseCode() {
     return $this->responseCode;
   }
 
   public function setFrameable($frameable) {
     $this->frameable = $frameable;
     return $this;
   }
 
   protected function encodeJSONForHTTPResponse(array $object) {
 
     $response = json_encode($object);
 
     // Prevent content sniffing attacks by encoding "<" and ">", so browsers
     // won't try to execute the document as HTML even if they ignore
     // Content-Type and X-Content-Type-Options. See T865.
     $response = str_replace(
       array('<', '>'),
       array('\u003c', '\u003e'),
       $response);
 
     return $response;
   }
 
   protected function addJSONShield($json_response, $use_javelin_shield) {
 
     // Add a shield to prevent "JSON Hijacking" attacks where an attacker
     // requests a JSON response using a normal <script /> tag and then uses
     // Object.prototype.__defineSetter__() or similar to read response data.
     // This header causes the browser to loop infinitely instead of handing over
     // sensitive data.
 
     // TODO: This is massively stupid: Javelin and Conduit use different
     // shields.
     $shield = $use_javelin_shield
       ? 'for (;;);'
       : 'for(;;);';
 
     $response = $shield.$json_response;
 
     return $response;
   }
 
   public function getCacheHeaders() {
     $headers = array();
     if ($this->cacheable) {
       $headers[] = array(
         'Expires',
         $this->formatEpochTimestampForHTTPHeader(time() + $this->cacheable));
     } else {
       $headers[] = array(
         'Cache-Control',
         'private, no-cache, no-store, must-revalidate');
+      $headers[] = array(
+        'Pragma',
+        'no-cache');
       $headers[] = array(
         'Expires',
         'Sat, 01 Jan 2000 00:00:00 GMT');
     }
 
     if ($this->lastModified) {
       $headers[] = array(
         'Last-Modified',
         $this->formatEpochTimestampForHTTPHeader($this->lastModified));
     }
 
     // IE has a feature where it may override an explicit Content-Type
     // declaration by inferring a content type. This can be a security risk
     // and we always explicitly transmit the correct Content-Type header, so
     // prevent IE from using inferred content types. This only offers protection
     // on recent versions of IE; IE6/7 and Opera currently ignore this header.
     $headers[] = array('X-Content-Type-Options', 'nosniff');
 
     return $headers;
   }
 
   private function formatEpochTimestampForHTTPHeader($epoch_timestamp) {
     return gmdate('D, d M Y H:i:s', $epoch_timestamp).' GMT';
   }
 
   abstract public function buildResponseString();
 
 }
diff --git a/src/applications/auth/oauth/provider/phabricator/PhabricatorOAuthProviderPhabricator.php b/src/applications/auth/oauth/provider/phabricator/PhabricatorOAuthProviderPhabricator.php
index 1d011a64b3..d5954d27e3 100644
--- a/src/applications/auth/oauth/provider/phabricator/PhabricatorOAuthProviderPhabricator.php
+++ b/src/applications/auth/oauth/provider/phabricator/PhabricatorOAuthProviderPhabricator.php
@@ -1,132 +1,143 @@
 <?php
 
 /*
  * Copyright 2012 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 PhabricatorOAuthProviderPhabricator
 extends PhabricatorOAuthProvider {
-
   private $userData;
 
+  public function getExtraAuthParameters() {
+    return array(
+      'response_type' => 'code',
+    );
+  }
+
+  public function getExtraTokenParameters() {
+    return array(
+      'grant_type' => 'authorization_code',
+    );
+
+  }
   public function decodeTokenResponse($response) {
     $decoded = json_decode($response, true);
     if (!is_array($decoded)) {
       throw new Exception('Invalid token response.');
     }
     return $decoded;
   }
 
   public function getProviderKey() {
     return self::PROVIDER_PHABRICATOR;
   }
 
   public function getProviderName() {
     return 'Phabricator';
   }
 
   public function isProviderEnabled() {
     return PhabricatorEnv::getEnvConfig('phabricator.auth-enabled');
   }
 
   public function isProviderLinkPermanent() {
     return PhabricatorEnv::getEnvConfig('phabricator.auth-permanent');
   }
 
   public function isProviderRegistrationEnabled() {
     return PhabricatorEnv::getEnvConfig('phabricator.registration-enabled');
   }
 
   public function getClientID() {
     return PhabricatorEnv::getEnvConfig('phabricator.application-id');
   }
 
   public function renderGetClientIDHelp() {
     return null;
   }
 
   public function getClientSecret() {
     return PhabricatorEnv::getEnvConfig('phabricator.application-secret');
   }
 
   public function renderGetClientSecretHelp() {
     return null;
   }
 
   public function getAuthURI() {
     return $this->getURI('/oauthserver/auth/');
   }
 
   public function getTestURIs() {
     return array(
       $this->getURI('/'),
       $this->getURI('/api/user.whoami/')
     );
   }
 
   public function getTokenURI() {
     return $this->getURI('/oauthserver/token/');
   }
 
   public function getUserInfoURI() {
     return $this->getURI('/api/user.whoami');
   }
 
   public function getMinimumScope() {
-    return 'email';
+    return 'whoami';
   }
 
   public function setUserData($data) {
     // need to strip the javascript shield from conduit
     $data = substr($data, 8);
     $data = json_decode($data, true);
     if (!is_array($data)) {
       throw new Exception('Invalid user data.');
     }
     $this->userData = $data['result'];
     return $this;
   }
 
   public function retrieveUserID() {
     return $this->userData['phid'];
   }
 
   public function retrieveUserEmail() {
     return $this->userData['email'];
   }
 
   public function retrieveUserAccountName() {
     return $this->userData['userName'];
   }
 
   public function retrieveUserProfileImage() {
     $uri = $this->userData['image'];
     return @file_get_contents($uri);
   }
 
   public function retrieveUserAccountURI() {
     return $this->userData['uri'];
   }
 
   public function retrieveUserRealName() {
     return $this->userData['realName'];
   }
 
   private function getURI($path) {
     return
       rtrim(PhabricatorEnv::getEnvConfig('phabricator.oauth-uri'), '/') .
       $path;
   }
 }
diff --git a/src/applications/oauthserver/controller/auth/PhabricatorOAuthServerAuthController.php b/src/applications/oauthserver/controller/auth/PhabricatorOAuthServerAuthController.php
index 836bfa9d13..1fc10c09ef 100644
--- a/src/applications/oauthserver/controller/auth/PhabricatorOAuthServerAuthController.php
+++ b/src/applications/oauthserver/controller/auth/PhabricatorOAuthServerAuthController.php
@@ -1,139 +1,215 @@
 <?php
 
 /*
  * Copyright 2012 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.
  */
 
 /**
  * @group oauthserver
  */
 final class PhabricatorOAuthServerAuthController
 extends PhabricatorAuthController {
 
   public function shouldRequireLogin() {
     return true;
   }
 
   public function processRequest() {
-    $request      = $this->getRequest();
-    $current_user = $request->getUser();
-    $server       = new PhabricatorOAuthServer($current_user);
-    $client_phid  = $request->getStr('client_id');
-    $scope        = $request->getStr('scope');
-    $redirect_uri = $request->getStr('redirect_uri');
-    $response     = new PhabricatorOAuthResponse();
-    $errors       = array();
+    $request       = $this->getRequest();
+    $current_user  = $request->getUser();
+    $server        = new PhabricatorOAuthServer();
+    $client_phid   = $request->getStr('client_id');
+    $scope         = $request->getStr('scope');
+    $redirect_uri  = $request->getStr('redirect_uri');
+    $state         = $request->getStr('state');
+    $response_type = $request->getStr('response_type');
+    $response      = new PhabricatorOAuthResponse();
 
+    // state is an opaque value the client sent us for their own purposes
+    // we just need to send it right back to them in the response!
+    if ($state) {
+      $response->setState($state);
+    }
     if (!$client_phid) {
-      return $response->setMalformed(
+      $response->setError('invalid_request');
+      $response->setErrorDescription(
         'Required parameter client_id not specified.'
       );
+      return $response;
     }
-    $client = id(new PhabricatorOAuthServerClient())
-      ->loadOneWhere('phid = %s', $client_phid);
-    if (!$client) {
-      return $response->setNotFound(
-        'Client with id '.$client_phid.' not found. '
-      );
-    }
-
-    $server->setClient($client);
-    if ($server->userHasAuthorizedClient()) {
-      $return_auth_code = true;
-      $unguarded_write  = AphrontWriteGuard::beginScopedUnguardedWrites();
-    } else if ($request->isFormPost()) {
-      $scope = PhabricatorOAuthServerScope::getScopesFromRequest($request);
-      $server->authorizeClient($scope);
-      $return_auth_code = true;
-      $unguarded_write  = null;
-    } else {
-      $return_auth_code = false;
-      $unguarded_write  = null;
-    }
-
-    if ($return_auth_code) {
-      // step 1 -- generate authorization code
-      $auth_code =
-        $server->generateAuthorizationCode();
+    $server->setUser($current_user);
 
-      // step 2 -- error or return it
-      if (!$auth_code) {
-        $errors[] = 'Failed to generate an authorization code. '.
-                    'Try again.';
+    // one giant try / catch around all the exciting database stuff so we
+    // can return a 'server_error' response if something goes wrong!
+    try {
+      $client = id(new PhabricatorOAuthServerClient())
+        ->loadOneWhere('phid = %s', $client_phid);
+      if (!$client) {
+        $response->setError('invalid_request');
+        $response->setErrorDescription(
+          'Client with id '.$client_phid.' not found.'
+        );
+        return $response;
+      }
+      $server->setClient($client);
+      if ($redirect_uri) {
+        $client_uri   = new PhutilURI($client->getRedirectURI());
+        $redirect_uri = new PhutilURI($redirect_uri);
+        if (!($server->validateSecondaryRedirectURI($redirect_uri,
+                                                    $client_uri))) {
+          $response->setError('invalid_request');
+          $response->setErrorDescription(
+            'The specified redirect URI is invalid. The redirect URI '.
+            'must be a fully-qualified domain with no fragments and '.
+            'must have the same domain and at least the same query '.
+            'parameters as the redirect URI the client registered.'
+          );
+          return $response;
+        }
+        $uri              = $redirect_uri;
+        $access_token_uri = $uri;
       } else {
-        $client_uri = new PhutilURI($client->getRedirectURI());
-        if (!$redirect_uri) {
-          $uri = $client_uri;
-        } else {
-          $redirect_uri = new PhutilURI($redirect_uri);
-          if ($redirect_uri->getDomain() !=
-              $client_uri->getDomain()) {
-            $uri = $client_uri;
-          } else {
-            $uri = $redirect_uri;
-          }
+        $uri              = new PhutilURI($client->getRedirectURI());
+        $access_token_uri = null;
+      }
+      // we've now validated this request enough overall such that we
+      // can safely redirect to the client with the response
+      $response->setClientURI($uri);
+
+      if (empty($response_type)) {
+        $response->setError('invalid_request');
+        $response->setErrorDescription(
+          'Required parameter response_type not specified.'
+        );
+        return $response;
+      }
+      if ($response_type != 'code') {
+        $response->setError('unsupported_response_type');
+        $response->setErrorDescription(
+          'The authorization server does not support obtaining an '.
+          'authorization code using the specified response_type. '.
+          'You must specify the response_type as "code".'
+        );
+        return $response;
+      }
+      if ($scope) {
+        if (!PhabricatorOAuthServerScope::validateScopesList($scope)) {
+          $response->setError('invalid_scope');
+          $response->setErrorDescription(
+            'The requested scope is invalid, unknown, or malformed.'
+          );
+          return $response;
         }
+        $scope = PhabricatorOAuthServerScope::scopesListToDict($scope);
+      }
 
-        $uri->setQueryParam('code', $auth_code->getCode());
-        return $response->setRedirect($uri);
+      $authorization = $server->userHasAuthorizedClient($scope);
+      if ($authorization) {
+        $return_auth_code = true;
+        $unguarded_write  = AphrontWriteGuard::beginScopedUnguardedWrites();
+      } else if ($request->isFormPost()) {
+        $scope = PhabricatorOAuthServerScope::getScopesFromRequest($request);
+        $authorization    = $server->authorizeClient($scope);
+        $return_auth_code = true;
+        $unguarded_write  = null;
+      } else {
+        $return_auth_code = false;
+        $unguarded_write  = null;
       }
-    }
-    unset($unguarded_write);
 
-    $error_view = null;
-    if ($errors) {
-      $error_view = new AphrontErrorView();
-      $error_view->setTitle('Authorization Code Errors');
-      $error_view->setErrors($errors);
+      if ($return_auth_code) {
+        // step 1 -- generate authorization code
+        $auth_code =
+          $server->generateAuthorizationCode($access_token_uri);
+
+        // step 2 return it
+        $content = array(
+          'code'  => $auth_code->getCode(),
+          'scope' => $authorization->getScopeString(),
+        );
+        $response->setContent($content);
+        return $response->setClientURI($uri);
+      }
+      unset($unguarded_write);
+    } catch (Exception $e) {
+      // Note we could try harder to determine between a server_error
+      // vs temporarily_unavailable.  Good enough though.
+      $response->setError('server_error');
+      $response->setErrorDescription(
+        'The authorization server encountered an unexpected condition '.
+        'which prevented it from fulfilling the request. '
+      );
+      return $response;
     }
 
+    // display time -- make a nice form for the user to grant the client
+    // access to the granularity specified by $scope
     $name  = phutil_escape_html($client->getName());
     $title = 'Authorize ' . $name . '?';
     $panel = new AphrontPanelView();
     $panel->setWidth(AphrontPanelView::WIDTH_FORM);
     $panel->setHeader($title);
 
     $description =
       "Do want to authorize {$name} to access your ".
       "Phabricator account data?";
 
-    $desired_scopes = array(
-      PhabricatorOAuthServerScope::SCOPE_WHOAMI => 1,
-      PhabricatorOAuthServerScope::SCOPE_OFFLINE_ACCESS => 1
+    if ($scope) {
+      $desired_scopes = $scope;
+      if (!PhabricatorOAuthServerScope::validateScopesDict($desired_scopes)) {
+        $response->setError('invalid_scope');
+        $response->setErrorDescription(
+          'The requested scope is invalid, unknown, or malformed.'
+        );
+        return $response;
+      }
+    } else {
+      $desired_scopes = array(
+        PhabricatorOAuthServerScope::SCOPE_WHOAMI         => 1,
+        PhabricatorOAuthServerScope::SCOPE_OFFLINE_ACCESS => 1
+      );
+    }
+
+    $cancel_uri = $this->getClientURI($client, $redirect_uri);
+    $cancel_params = array(
+      'error' => 'access_denied',
+      'error_description' =>
+        'The resource owner (aka the user) denied the request.'
     );
+    $cancel_uri->setQueryParams($cancel_params);
+
     $form = id(new AphrontFormView())
       ->setUser($current_user)
       ->appendChild(
         id(new AphrontFormStaticControl())
         ->setValue($description)
       )
       ->appendChild(
         PhabricatorOAuthServerScope::getCheckboxControl()
       )
       ->appendChild(
         id(new AphrontFormSubmitControl())
         ->setValue('Authorize')
-        ->addCancelButton('/')
+        ->addCancelButton($cancel_uri)
       );
-    // TODO -- T889 (make "cancel" do something more sensible)
 
     $panel->appendChild($form);
 
     return $this->buildStandardPageResponse(
-      array($error_view,
-            $panel),
+            $panel,
       array('title' => $title));
   }
+
 }
diff --git a/src/applications/oauthserver/controller/auth/__init__.php b/src/applications/oauthserver/controller/auth/__init__.php
index 8a0cd0c8a6..3ab5ab427a 100644
--- a/src/applications/oauthserver/controller/auth/__init__.php
+++ b/src/applications/oauthserver/controller/auth/__init__.php
@@ -1,26 +1,25 @@
 <?php
 /**
  * This file is automatically generated. Lint this module to rebuild it.
  * @generated
  */
 
 
 
 phutil_require_module('phabricator', 'aphront/writeguard');
 phutil_require_module('phabricator', 'applications/auth/controller/base');
 phutil_require_module('phabricator', 'applications/oauthserver/response');
 phutil_require_module('phabricator', 'applications/oauthserver/scope');
 phutil_require_module('phabricator', 'applications/oauthserver/server');
 phutil_require_module('phabricator', 'applications/oauthserver/storage/client');
 phutil_require_module('phabricator', 'view/form/base');
 phutil_require_module('phabricator', 'view/form/control/static');
 phutil_require_module('phabricator', 'view/form/control/submit');
-phutil_require_module('phabricator', 'view/form/error');
 phutil_require_module('phabricator', 'view/layout/panel');
 
 phutil_require_module('phutil', 'markup');
 phutil_require_module('phutil', 'parser/uri');
 phutil_require_module('phutil', 'utils');
 
 
 phutil_require_source('PhabricatorOAuthServerAuthController.php');
diff --git a/src/applications/oauthserver/controller/client/edit/PhabricatorOAuthClientEditController.php b/src/applications/oauthserver/controller/client/edit/PhabricatorOAuthClientEditController.php
index 20a71b0880..9d3063274b 100644
--- a/src/applications/oauthserver/controller/client/edit/PhabricatorOAuthClientEditController.php
+++ b/src/applications/oauthserver/controller/client/edit/PhabricatorOAuthClientEditController.php
@@ -1,197 +1,196 @@
 <?php
 
 /*
  * Copyright 2012 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.
  */
 
 /**
  * @group oauthserver
  */
 final class PhabricatorOAuthClientEditController
 extends PhabricatorOAuthClientBaseController {
 
   private $isEdit;
   protected function isClientEdit() {
     return $this->isEdit;
   }
   private function setIsClientEdit($is_edit) {
     $this->isEdit = (bool) $is_edit;
     return $this;
   }
 
   protected function getExtraClientFilters() {
     if ($this->isClientEdit()) {
       $filters = array(
         array('url'   => $this->getFilter(),
               'label' => 'Edit Client')
       );
     } else {
       $filters = array();
     }
     return $filters;
   }
 
   public function getFilter() {
     if ($this->isClientEdit()) {
       $filter = 'client/edit/'.$this->getClientPHID();
     } else {
       $filter = 'client/create';
     }
     return $filter;
   }
 
   public function processRequest() {
     $request       = $this->getRequest();
     $current_user  = $request->getUser();
     $error         = null;
     $bad_redirect  = false;
     $phid          = $this->getClientPHID();
     // if we have a phid, then we're editing
     $this->setIsClientEdit($phid);
 
     if ($this->isClientEdit()) {
       $client = id(new PhabricatorOAuthServerClient())
         ->loadOneWhere('phid = %s',
                        $phid);
       $title  = 'Edit OAuth Client';
       // validate the client
       if (empty($client)) {
         return new Aphront404Response();
       }
       if ($client->getCreatorPHID() != $current_user->getPHID()) {
         $message = 'Access denied to edit client with id '.$phid.'. '.
                    'Only the user who created the client has permission to '.
                    'edit the client.';
         return id(new Aphront403Response())
           ->setForbiddenText($message);
       }
       $submit_button = 'Save OAuth Client';
+      $secret        = null;
     // new client - much simpler
     } else {
-      $client = new PhabricatorOAuthServerClient();
-      $title  = 'Create OAuth Client';
+      $client        = new PhabricatorOAuthServerClient();
+      $title         = 'Create OAuth Client';
       $submit_button = 'Create OAuth Client';
+      $secret        = Filesystem::readRandomCharacters(32);
     }
 
     if ($request->isFormPost()) {
       $redirect_uri = $request->getStr('redirect_uri');
       $client->setName($request->getStr('name'));
       $client->setRedirectURI($redirect_uri);
-      $client->setSecret(Filesystem::readRandomCharacters(32));
+      if ($secret) {
+        $client->setSecret($secret);
+      }
       $client->setCreatorPHID($current_user->getPHID());
       $uri = new PhutilURI($redirect_uri);
-      if (!$this->validateRedirectURI($uri)) {
+      $server = new PhabricatorOAuthServer();
+      if (!$server->validateRedirectURI($uri)) {
         $error = new AphrontErrorView();
         $error->setSeverity(AphrontErrorView::SEVERITY_ERROR);
         $error->setTitle(
-          'Redirect URI must be a fully qualified domain name.'
+          'Redirect URI must be a fully qualified domain name '.
+          'with no fragments. See '.
+          'http://tools.ietf.org/html/draft-ietf-oauth-v2-23#section-3.1.2 '.
+          'for more information on the correct format.'
         );
         $bad_redirect = true;
       } else {
         $client->save();
         if ($this->isClientEdit()) {
           return id(new AphrontRedirectResponse())
             ->setURI('/oauthserver/client/?edited='.$phid);
         } else {
           return id(new AphrontRedirectResponse())
             ->setURI('/oauthserver/client/?new='.$phid);
         }
       }
     }
 
     $panel = new AphrontPanelView();
     if ($this->isClientEdit()) {
       $delete_button = phutil_render_tag(
         'a',
         array(
           'href' => $client->getDeleteURI(),
           'class' => 'grey button',
         ),
         'Delete OAuth Client');
       $panel->addButton($delete_button);
     }
     $panel->setHeader($title);
 
     $form = id(new AphrontFormView())
       ->setUser($current_user)
       ->appendChild(
         id(new AphrontFormTextControl())
         ->setLabel('Name')
         ->setName('name')
         ->setValue($client->getName())
       );
     if ($this->isClientEdit()) {
       $form
         ->appendChild(
           id(new AphrontFormTextControl())
           ->setLabel('ID')
           ->setValue($phid)
         )
         ->appendChild(
-          id(new AphrontFormTextControl())
+          id(new AphrontFormStaticControl())
           ->setLabel('Secret')
           ->setValue($client->getSecret())
         );
     }
     $form
       ->appendChild(
         id(new AphrontFormTextControl())
         ->setLabel('Redirect URI')
         ->setName('redirect_uri')
         ->setValue($client->getRedirectURI())
         ->setError($bad_redirect)
       );
     if ($this->isClientEdit()) {
       $created = phabricator_datetime($client->getDateCreated(),
                                       $current_user);
       $updated = phabricator_datetime($client->getDateModified(),
                                       $current_user);
       $form
         ->appendChild(
           id(new AphrontFormStaticControl())
           ->setLabel('Created')
           ->setValue($created)
         )
         ->appendChild(
           id(new AphrontFormStaticControl())
           ->setLabel('Last Updated')
           ->setValue($updated)
         );
     }
     $form
       ->appendChild(
         id(new AphrontFormSubmitControl())
         ->setValue($submit_button)
       );
 
     $panel->appendChild($form);
     return $this->buildStandardPageResponse(
       array($error,
             $panel
       ),
       array('title' => $title)
     );
   }
 
-  private function validateRedirectURI(PhutilURI $uri) {
-    if (PhabricatorEnv::isValidRemoteWebResource($uri)) {
-      if ($uri->getDomain()) {
-        return true;
-      }
-    }
-    return false;
-  }
-
 }
diff --git a/src/applications/oauthserver/controller/client/edit/__init__.php b/src/applications/oauthserver/controller/client/edit/__init__.php
index d7ec2ce1bb..869cec1bb1 100644
--- a/src/applications/oauthserver/controller/client/edit/__init__.php
+++ b/src/applications/oauthserver/controller/client/edit/__init__.php
@@ -1,29 +1,29 @@
 <?php
 /**
  * This file is automatically generated. Lint this module to rebuild it.
  * @generated
  */
 
 
 
 phutil_require_module('phabricator', 'aphront/response/403');
 phutil_require_module('phabricator', 'aphront/response/404');
 phutil_require_module('phabricator', 'aphront/response/redirect');
 phutil_require_module('phabricator', 'applications/oauthserver/controller/client/base');
+phutil_require_module('phabricator', 'applications/oauthserver/server');
 phutil_require_module('phabricator', 'applications/oauthserver/storage/client');
-phutil_require_module('phabricator', 'infrastructure/env');
 phutil_require_module('phabricator', 'view/form/base');
 phutil_require_module('phabricator', 'view/form/control/static');
 phutil_require_module('phabricator', 'view/form/control/submit');
 phutil_require_module('phabricator', 'view/form/control/text');
 phutil_require_module('phabricator', 'view/form/error');
 phutil_require_module('phabricator', 'view/layout/panel');
 phutil_require_module('phabricator', 'view/utils');
 
 phutil_require_module('phutil', 'filesystem');
 phutil_require_module('phutil', 'markup');
 phutil_require_module('phutil', 'parser/uri');
 phutil_require_module('phutil', 'utils');
 
 
 phutil_require_source('PhabricatorOAuthClientEditController.php');
diff --git a/src/applications/oauthserver/controller/client/list/PhabricatorOAuthClientListController.php b/src/applications/oauthserver/controller/client/list/PhabricatorOAuthClientListController.php
index 4ab49702eb..43b389f51d 100644
--- a/src/applications/oauthserver/controller/client/list/PhabricatorOAuthClientListController.php
+++ b/src/applications/oauthserver/controller/client/list/PhabricatorOAuthClientListController.php
@@ -1,140 +1,143 @@
 <?php
 
 /*
  * Copyright 2012 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.
  */
 
 /**
  * @group oauthserver
  */
 final class PhabricatorOAuthClientListController
 extends PhabricatorOAuthClientBaseController {
 
   public function getFilter() {
     return 'client';
   }
 
   public function processRequest() {
     $title        = 'OAuth Clients';
     $request      = $this->getRequest();
     $current_user = $request->getUser();
     $clients      = id(new PhabricatorOAuthServerClient())
       ->loadAllWhere('creatorPHID = %s',
                      $current_user->getPHID());
 
     $rows      = array();
     $rowc      = array();
     $highlight = $this->getHighlightPHIDs();
     foreach ($clients as $client) {
       $row = array(
         phutil_render_tag(
           'a',
           array(
             'href' => $client->getViewURI(),
           ),
           phutil_escape_html($client->getName())
         ),
         $client->getPHID(),
+        $client->getSecret(),
         phutil_render_tag(
           'a',
           array(
             'href' => $client->getRedirectURI(),
           ),
           phutil_escape_html($client->getRedirectURI())
         ),
         phutil_render_tag(
           'a',
           array(
             'class' => 'small button grey',
             'href'  => $client->getEditURI(),
           ),
           'Edit'
         ),
       );
 
       $rows[] = $row;
       if (isset($highlight[$client->getPHID()])) {
         $rowc[] = 'highlighted';
       } else {
         $rowc[] = '';
       }
     }
 
     $panel = $this->buildClientList($rows, $rowc, $title);
 
     return $this->buildStandardPageResponse(
       array($this->getNoticeView(),
             $panel),
       array('title' => $title)
     );
   }
 
   private function buildClientList($rows, $rowc, $title) {
     $table = new AphrontTableView($rows);
     $table->setRowClasses($rowc);
     $table->setHeaders(
       array(
         'Client',
         'ID',
+        'Secret',
         'Redirect URI',
         '',
       ));
     $table->setColumnClasses(
       array(
         '',
         '',
         '',
+        '',
         'action',
       ));
     if (empty($rows)) {
       $table->setNoDataString(
         'You have not created any clients for this OAuthServer.'
       );
     }
 
     $panel = new AphrontPanelView();
     $panel->appendChild($table);
     $panel->setHeader($title);
 
     return $panel;
   }
 
   private function getNoticeView() {
     $edited  = $this->getRequest()->getStr('edited');
     $new     = $this->getRequest()->getStr('new');
     $deleted = $this->getRequest()->getBool('deleted');
     if ($edited) {
       $edited = phutil_escape_html($edited);
       $title  = 'Successfully edited client with id '.$edited.'.';
     } else if ($new) {
       $new   = phutil_escape_html($new);
       $title = 'Successfully created client with id '.$new.'.';
     } else if ($deleted) {
       $title = 'Successfully deleted client.';
     } else {
       $title = null;
     }
 
     if ($title) {
       $view   = new AphrontErrorView();
       $view->setTitle($title);
       $view->setSeverity(AphrontErrorView::SEVERITY_NOTICE);
     } else {
       $view = null;
     }
 
     return $view;
   }
 }
diff --git a/src/applications/oauthserver/controller/token/PhabricatorOAuthServerTokenController.php b/src/applications/oauthserver/controller/token/PhabricatorOAuthServerTokenController.php
index d61ba06f96..7bc06b8785 100644
--- a/src/applications/oauthserver/controller/token/PhabricatorOAuthServerTokenController.php
+++ b/src/applications/oauthserver/controller/token/PhabricatorOAuthServerTokenController.php
@@ -1,104 +1,160 @@
 <?php
 
 /*
  * Copyright 2012 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.
  */
 
 /**
  * @group oauthserver
  */
 final class PhabricatorOAuthServerTokenController
 extends PhabricatorAuthController {
 
   public function shouldRequireLogin() {
     return false;
   }
 
   public function processRequest() {
     $request       = $this->getRequest();
+    $grant_type    = $request->getStr('grant_type');
     $code          = $request->getStr('code');
+    $redirect_uri  = $request->getStr('redirect_uri');
     $client_phid   = $request->getStr('client_id');
     $client_secret = $request->getStr('client_secret');
     $response      = new PhabricatorOAuthResponse();
     $server        = new PhabricatorOAuthServer();
+    if ($grant_type != 'authorization_code') {
+      $response->setError('unsupported_grant_type');
+      $response->setErrorDescription(
+        'Only grant_type authorization_code is supported.'
+      );
+      return $response;
+    }
     if (!$code) {
-      return $response->setMalformed(
+      $response->setError('invalid_request');
+      $response->setErrorDescription(
         'Required parameter code missing.'
       );
+      return $response;
     }
     if (!$client_phid) {
-      return $response->setMalformed(
+      $response->setError('invalid_request');
+      $response->setErrorDescription(
         'Required parameter client_id missing.'
       );
+      return $response;
     }
     if (!$client_secret) {
-      return $response->setMalformed(
+      $response->setError('invalid_request');
+      $response->setErrorDescription(
         'Required parameter client_secret missing.'
       );
+      return $response;
     }
+    // one giant try / catch around all the exciting database stuff so we
+    // can return a 'server_error' response if something goes wrong!
+    try {
+      $auth_code = id(new PhabricatorOAuthServerAuthorizationCode())
+        ->loadOneWhere('code = %s',
+                       $code);
+      if (!$auth_code) {
+        $response->setError('invalid_grant');
+        $response->setErrorDescription(
+          'Authorization code '.$code.' not found.'
+        );
+        return $response;
+      }
 
-    $client = id(new PhabricatorOAuthServerClient())
-      ->loadOneWhere('phid = %s', $client_phid);
-    if (!$client) {
-      return $response->setNotFound(
-        'Client with client_id '.$client_phid.' not found.'
-      );
-    }
-    $server->setClient($client);
+      // if we have an auth code redirect URI, there must be a redirect_uri
+      // in the request and it must match the auth code redirect uri *exactly*
+      $auth_code_redirect_uri = $auth_code->getRedirectURI();
+      if ($auth_code_redirect_uri) {
+        $auth_code_redirect_uri = new PhutilURI($auth_code_redirect_uri);
+        $redirect_uri           = new PhutilURI($redirect_uri);
+        if (!$redirect_uri->getDomain() ||
+             $redirect_uri != $auth_code_redirect_uri) {
+          $response->setError('invalid_grant');
+          $response->setErrorDescription(
+            'Redirect uri in request must exactly match redirect uri '.
+            'from authorization code.'
+          );
+          return $response;
+        }
+      } else if ($redirect_uri) {
+        $response->setError('invalid_grant');
+        $response->setErrorDescription(
+          'Redirect uri in request and no redirect uri in authorization '.
+          'code. The two must exactly match.'
+        );
+        return $response;
+      }
 
-    $auth_code = id(new PhabricatorOAuthServerAuthorizationCode())
-      ->loadOneWhere('code = %s', $code);
-    if (!$auth_code) {
-      return $response->setNotFound(
-        'Authorization code '.$code.' not found.'
-      );
-    }
+      $client = id(new PhabricatorOAuthServerClient())
+        ->loadOneWhere('phid = %s',
+                       $client_phid);
+      if (!$client) {
+        $response->setError('invalid_client');
+        $response->setErrorDescription(
+          'Client with client_id '.$client_phid.' not found.'
+        );
+        return $response;
+      }
+      $server->setClient($client);
 
-    $user_phid = $auth_code->getUserPHID();
-    $user = id(new PhabricatorUser())
-      ->loadOneWhere('phid = %s', $user_phid);
-    if (!$user) {
-      return $response->setNotFound(
-        'User with phid '.$user_phid.' not found.'
-      );
-    }
-    $server->setUser($user);
+      $user_phid = $auth_code->getUserPHID();
+      $user = id(new PhabricatorUser())
+        ->loadOneWhere('phid = %s', $user_phid);
+      if (!$user) {
+        $response->setError('invalid_grant');
+        $response->setErrorDescription(
+          'User with phid '.$user_phid.' not found.'
+        );
+        return $response;
+      }
+      $server->setUser($user);
 
-    $test_code = new PhabricatorOAuthServerAuthorizationCode();
-    $test_code->setClientSecret($client_secret);
-    $test_code->setClientPHID($client_phid);
-    $is_good_code = $server->validateAuthorizationCode($auth_code,
-                                                       $test_code);
-    if (!$is_good_code) {
-      return $response->setMalformed(
-        'Invalid authorization code '.$code.'.'
-      );
-    }
+      $test_code = new PhabricatorOAuthServerAuthorizationCode();
+      $test_code->setClientSecret($client_secret);
+      $test_code->setClientPHID($client_phid);
+      $is_good_code = $server->validateAuthorizationCode($auth_code,
+                                                         $test_code);
+      if (!$is_good_code) {
+        $response->setError('invalid_grant');
+        $response->setErrorDescription(
+          'Invalid authorization code '.$code.'.'
+        );
+        return $response;
+      }
 
-    $scope = AphrontWriteGuard::beginScopedUnguardedWrites();
-    $access_token = $server->generateAccessToken();
-    if ($access_token) {
+      $unguarded    = AphrontWriteGuard::beginScopedUnguardedWrites();
+      $access_token = $server->generateAccessToken();
       $auth_code->delete();
+      unset($unguarded);
       $result = array(
-         'access_token' => $access_token->getToken(),
-         'token_type'   => 'Bearer',
-         'expires_in'   => PhabricatorOAuthServer::ACCESS_TOKEN_TIMEOUT,
-       );
+        'access_token' => $access_token->getToken(),
+        'token_type'   => 'Bearer',
+        'expires_in'   => PhabricatorOAuthServer::ACCESS_TOKEN_TIMEOUT,
+      );
       return $response->setContent($result);
+    } catch (Exception $e) {
+      $response->setError('server_error');
+      $response->setErrorDescription(
+        'The authorization server encountered an unexpected condition '.
+        'which prevented it from fulfilling the request.'
+      );
+      return $response;
     }
-
-    return $response->setMalformed('Request is malformed in some way.');
   }
 }
diff --git a/src/applications/oauthserver/controller/token/__init__.php b/src/applications/oauthserver/controller/token/__init__.php
index e7cb9e94a3..1ac94adb82 100644
--- a/src/applications/oauthserver/controller/token/__init__.php
+++ b/src/applications/oauthserver/controller/token/__init__.php
@@ -1,20 +1,21 @@
 <?php
 /**
  * This file is automatically generated. Lint this module to rebuild it.
  * @generated
  */
 
 
 
 phutil_require_module('phabricator', 'aphront/writeguard');
 phutil_require_module('phabricator', 'applications/auth/controller/base');
 phutil_require_module('phabricator', 'applications/oauthserver/response');
 phutil_require_module('phabricator', 'applications/oauthserver/server');
 phutil_require_module('phabricator', 'applications/oauthserver/storage/authorizationcode');
 phutil_require_module('phabricator', 'applications/oauthserver/storage/client');
 phutil_require_module('phabricator', 'applications/people/storage/user');
 
+phutil_require_module('phutil', 'parser/uri');
 phutil_require_module('phutil', 'utils');
 
 
 phutil_require_source('PhabricatorOAuthServerTokenController.php');
diff --git a/src/applications/oauthserver/response/PhabricatorOAuthResponse.php b/src/applications/oauthserver/response/PhabricatorOAuthResponse.php
index 6f01c4b0d2..49f574d47b 100644
--- a/src/applications/oauthserver/response/PhabricatorOAuthResponse.php
+++ b/src/applications/oauthserver/response/PhabricatorOAuthResponse.php
@@ -1,93 +1,123 @@
 <?php
 
 /*
  * Copyright 2012 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.
  */
 
 /*
 * @group oauthserver
 */
 final class PhabricatorOAuthResponse extends AphrontResponse {
 
+  private $state;
   private $content;
-  private $uri;
+  private $clientURI;
+  private $error;
+  private $errorDescription;
 
-  public function setContent($content) {
-    $this->content = $content;
+  private function getState() {
+    return $this->state;
+  }
+  public function setState($state) {
+    $this->state = $state;
     return $this;
   }
+
   private function getContent() {
     return $this->content;
   }
+  public function setContent($content) {
+    $this->content = $content;
+    return $this;
+  }
 
-  private function setURI($uri) {
-    $this->uri = $uri;
+  private function getClientURI() {
+    return $this->clientURI;
+  }
+  public function setClientURI(PhutilURI $uri) {
+    $this->setHTTPResponseCode(302);
+    $this->clientURI = $uri;
     return $this;
   }
-  private function getURI() {
-    return $this->uri;
+  private function getFullURI() {
+    $base_uri     = $this->getClientURI();
+    $query_params = $this->buildResponseDict();
+    foreach ($query_params as $key => $value) {
+      $base_uri->setQueryParam($key, $value);
+    }
+    return $base_uri;
   }
 
-  public function setMalformed($malformed) {
-    if ($malformed) {
+  private function getError() {
+    return $this->error;
+  }
+  public function setError($error) {
+    // errors sometimes redirect to the client (302) but otherwise
+    // the spec says all code 400
+    if (!$this->getClientURI()) {
       $this->setHTTPResponseCode(400);
-      $this->setContent(array('error' => $malformed));
     }
+    $this->error = $error;
     return $this;
   }
 
-  public function setNotFound($not_found) {
-    if ($not_found) {
-      $this->setHTTPResponseCode(404);
-      $this->setContent(array('error' => $not_found));
-    }
-    return $this;
+  private function getErrorDescription() {
+    return $this->errorDescription;
   }
-
-  public function setRedirect(PhutilURI $uri) {
-    if ($uri) {
-      $this->setHTTPResponseCode(302);
-      $this->setURI($uri);
-      $this->setContent(null);
-    }
+  public function setErrorDescription($error_description) {
+    $this->errorDescription = $error_description;
     return $this;
   }
 
   public function __construct() {
-    $this->setHTTPResponseCode(200);
+    $this->setHTTPResponseCode(200);      // assume the best
     return $this;
   }
 
   public function getHeaders() {
     $headers = array(
       array('Content-Type', 'application/json'),
     );
-    if ($this->getURI()) {
-      $headers[] = array('Location', $this->getURI());
+    if ($this->getClientURI()) {
+      $headers[] = array('Location', $this->getFullURI());
     }
     // TODO -- T844 set headers with X-Auth-Scopes, etc
     $headers = array_merge(parent::getHeaders(), $headers);
     return $headers;
   }
 
-  public function buildResponseString() {
+  private function buildResponseDict() {
+    if ($this->getError()) {
+      $content = array(
+        'error'             => $this->getError(),
+        'error_description' => $this->getErrorDescription(),
+      );
+      $this->setContent($content);
+    }
+
     $content = $this->getContent();
-    if ($content) {
-      return $this->encodeJSONForHTTPResponse($content);
+    if (!$content) {
+      return '';
+    }
+    if ($this->getState()) {
+      $content['state'] = $this->getState();
     }
-    return '';
+    return $content;
   }
 
+  public function buildResponseString() {
+    return $this->encodeJSONForHTTPResponse($this->buildResponseDict());
+  }
 }
diff --git a/src/applications/oauthserver/scope/PhabricatorOAuthServerScope.php b/src/applications/oauthserver/scope/PhabricatorOAuthServerScope.php
index 22246549ab..d57905a091 100644
--- a/src/applications/oauthserver/scope/PhabricatorOAuthServerScope.php
+++ b/src/applications/oauthserver/scope/PhabricatorOAuthServerScope.php
@@ -1,81 +1,117 @@
 <?php
 
 /*
  * Copyright 2012 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 PhabricatorOAuthServerScope {
 
   const SCOPE_OFFLINE_ACCESS = 'offline_access';
   const SCOPE_WHOAMI         = 'whoami';
   const SCOPE_NOT_ACCESSIBLE = 'not_accessible';
 
   /*
    * Note this does not contain SCOPE_NOT_ACCESSIBLE which is magic
    * used to simplify code for data that is not currently accessible
    * via OAuth.
    */
   static public function getScopesDict() {
     return array(
       self::SCOPE_OFFLINE_ACCESS => 1,
       self::SCOPE_WHOAMI         => 1,
     );
   }
 
   static public function getCheckboxControl($current_scopes) {
     $scopes = self::getScopesDict();
     $scope_keys = array_keys($scopes);
     sort($scope_keys);
 
     $checkboxes = new AphrontFormCheckboxControl();
     foreach ($scope_keys as $scope) {
       $checkboxes->addCheckbox(
         $name = $scope,
         $value = 1,
         $label = self::getCheckboxLabel($scope),
         $checked = isset($current_scopes[$scope])
       );
     }
     $checkboxes->setLabel('Scope');
 
     return $checkboxes;
   }
 
   static private function getCheckboxLabel($scope) {
     $label = null;
     switch ($scope) {
       case self::SCOPE_OFFLINE_ACCESS:
         $label = 'Make access tokens granted to this client never expire.';
         break;
       case self::SCOPE_WHOAMI:
         $label = 'Read access to Conduit method user.whoami.';
         break;
     }
 
     return $label;
   }
 
   static public function getScopesFromRequest(AphrontRequest $request) {
     $scopes = self::getScopesDict();
     $requested_scopes = array();
     foreach ($scopes as $scope => $bit) {
       if ($request->getBool($scope)) {
         $requested_scopes[$scope] = 1;
       }
     }
     return $requested_scopes;
   }
 
+  /**
+   * A scopes list is considered valid if each scope is a known scope
+   * and each scope is seen only once.  Otherwise, the list is invalid.
+   */
+  static public function validateScopesList($scope_list) {
+    $scopes       = explode(' ', $scope_list);
+    $known_scopes = self::getScopesDict();
+    $seen_scopes  = array();
+    foreach ($scopes as $scope) {
+      if (!isset($known_scopes[$scope])) {
+        return false;
+      }
+      if (isset($seen_scopes[$scope])) {
+        return false;
+      }
+      $seen_scopes[$scope] = 1;
+    }
+
+    return true;
+  }
+
+  /**
+   * A scopes dictionary is considered valid if each key is a known scope.
+   * Otherwise, the dictionary is invalid.
+   */
+  static public function validateScopesDict($scope_dict) {
+    $known_scopes   = self::getScopesDict();
+    $unknown_scopes = array_diff_key($scope_dict,
+                                     $known_scopes);
+    return empty($unknown_scopes);
+  }
+
+  static public function scopesListToDict($scope_list) {
+    return array_fill_keys($scope_list, 1);
+  }
+
 }
diff --git a/src/applications/oauthserver/server/PhabricatorOAuthServer.php b/src/applications/oauthserver/server/PhabricatorOAuthServer.php
index d60e3c4323..ea9d15859c 100644
--- a/src/applications/oauthserver/server/PhabricatorOAuthServer.php
+++ b/src/applications/oauthserver/server/PhabricatorOAuthServer.php
@@ -1,208 +1,252 @@
 <?php
 
 /*
  * Copyright 2012 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.
  */
 
 /**
  * Implements core OAuth 2.0 Server logic.
  *
  * This class should be used behind business logic that parses input to
  * determine pertinent @{class:PhabricatorUser} $user,
  * @{class:PhabricatorOAuthServerClient} $client(s),
  * @{class:PhabricatorOAuthServerAuthorizationCode} $code(s), and.
  * @{class:PhabricatorOAuthServerAccessToken} $token(s).
  *
  * For an OAuth 2.0 server, there are two main steps:
  *
  * 1) Authorization - the user authorizes a given client to access the data
  * the OAuth 2.0 server protects.  Once this is achieved / if it has
  * been achived already, the OAuth server sends the client an authorization
  * code.
  * 2) Access Token - the client should send the authorization code received in
  * step 1 along with its id and secret to the OAuth server to receive an
  * access token.  This access token can later be used to access Phabricator
  * data on behalf of the user.
  *
  * @task auth Authorizing @{class:PhabricatorOAuthServerClient}s and
  *            generating @{class:PhabricatorOAuthServerAuthorizationCode}s
  * @task token Validating @{class:PhabricatorOAuthServerAuthorizationCode}s
  *             and generating @{class:PhabricatorOAuthServerAccessToken}s
  * @task internal Internals
  *
  * @group oauthserver
  */
 final class PhabricatorOAuthServer {
 
   const AUTHORIZATION_CODE_TIMEOUT = 300;
   const ACCESS_TOKEN_TIMEOUT       = 3600;
 
   private $user;
   private $client;
 
   /**
    * @group internal
    */
   private function getUser() {
     if (!$this->user) {
       throw new Exception('You must setUser before you can getUser!');
     }
     return $this->user;
   }
 
   public function setUser(PhabricatorUser $user) {
     $this->user = $user;
     return $this;
   }
 
   /**
    * @group internal
    */
   private function getClient() {
     if (!$this->client) {
       throw new Exception('You must setClient before you can getClient!');
     }
     return $this->client;
   }
 
   public function setClient(PhabricatorOAuthServerClient $client) {
     $this->client = $client;
     return $this;
   }
 
   /**
    * @task auth
    */
-  public function userHasAuthorizedClient() {
+  public function userHasAuthorizedClient(array $scope) {
 
     $authorization = id(new PhabricatorOAuthClientAuthorization())->
       loadOneWhere('userPHID = %s AND clientPHID = %s',
                    $this->getUser()->getPHID(),
-                   $this->getClient->getPHID());
+                   $this->getClient()->getPHID());
 
-    if (empty($authorization)) {
+    if ($scope) {
+      $missing_scope = array_diff_key($scope,
+                                      $authorization->getScope());
+    } else {
+      $missing_scope = false;
+    }
+
+    if ($missing_scope) {
       return false;
     }
 
-    return true;
+    return $authorization;
   }
 
   /**
    * @task auth
    */
   public function authorizeClient(array $scope) {
     $authorization = new PhabricatorOAuthClientAuthorization();
     $authorization->setUserPHID($this->getUser()->getPHID());
     $authorization->setClientPHID($this->getClient()->getPHID());
     $authorization->setScope($scope);
     $authorization->save();
 
     return $authorization;
   }
 
   /**
    * @task auth
    */
-  public function generateAuthorizationCode() {
+  public function generateAuthorizationCode(PhutilURI $redirect_uri) {
 
     $code   = Filesystem::readRandomCharacters(32);
     $client = $this->getClient();
 
     $authorization_code = new PhabricatorOAuthServerAuthorizationCode();
     $authorization_code->setCode($code);
     $authorization_code->setClientPHID($client->getPHID());
     $authorization_code->setClientSecret($client->getSecret());
     $authorization_code->setUserPHID($this->getUser()->getPHID());
+    $authorization_code->setRedirectURI((string) $redirect_uri);
     $authorization_code->save();
 
     return $authorization_code;
   }
 
   /**
    * @task token
    */
   public function generateAccessToken() {
 
     $token = Filesystem::readRandomCharacters(32);
 
     $access_token = new PhabricatorOAuthServerAccessToken();
     $access_token->setToken($token);
     $access_token->setUserPHID($this->getUser()->getPHID());
     $access_token->setClientPHID($this->getClient()->getPHID());
     $access_token->save();
 
     return $access_token;
   }
 
   /**
    * @task token
    */
   public function validateAuthorizationCode(
     PhabricatorOAuthServerAuthorizationCode $test_code,
     PhabricatorOAuthServerAuthorizationCode $valid_code) {
 
     // check that all the meta data matches
     if ($test_code->getClientPHID() != $valid_code->getClientPHID()) {
       return false;
     }
     if ($test_code->getClientSecret() != $valid_code->getClientSecret()) {
       return false;
     }
 
     // check that the authorization code hasn't timed out
     $created_time = $test_code->getDateCreated();
     $must_be_used_by = $created_time + self::AUTHORIZATION_CODE_TIMEOUT;
     return (time() < $must_be_used_by);
   }
 
   /**
    * @task token
    */
   public function validateAccessToken(
     PhabricatorOAuthServerAccessToken $token,
     $required_scope) {
 
     $created_time    = $token->getDateCreated();
     $must_be_used_by = $created_time + self::ACCESS_TOKEN_TIMEOUT;
     $expired         = time() > $must_be_used_by;
     $authorization   = id(new PhabricatorOAuthClientAuthorization())
                          ->loadOneWhere(
                            'userPHID = %s AND clientPHID = %s',
                            $token->getUserPHID(),
                            $token->getClientPHID());
 
     if (!$authorization) {
       return false;
     }
     $token_scope = $authorization->getScope();
     if (!isset($token_scope[$required_scope])) {
       return false;
     }
 
+    $valid = true;
     if ($expired) {
       $valid = false;
       // check if the scope includes "offline_access", which makes the
       // token valid despite being expired
       if (isset(
         $token_scope[PhabricatorOAuthServerScope::SCOPE_OFFLINE_ACCESS]
       )) {
         $valid = true;
       }
     }
 
     return $valid;
   }
 
+  /**
+   * See http://tools.ietf.org/html/draft-ietf-oauth-v2-23#section-3.1.2
+   * for details on what makes a given redirect URI "valid".
+   */
+  public function validateRedirectURI(PhutilURI $uri) {
+    if (PhabricatorEnv::isValidRemoteWebResource($uri)) {
+      if ($uri->getFragment()) {
+        return false;
+      }
+      if ($uri->getDomain()) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * If there's a URI specified in an OAuth request, it must be validated in
+   * its own right. Further, it must have the same domain and (at least) the
+   * same query parameters as the primary URI.
+   */
+  public function validateSecondaryRedirectURI(PhutilURI $secondary_uri,
+                                               PhutilURI $primary_uri) {
+    $valid = $this->validateRedirectURI($secondary_uri);
+    if ($valid) {
+      $valid_domain    = ($secondary_uri->getDomain() ==
+                          $primary_uri->getDomain());
+      $good_params     = $primary_uri->getQueryParams();
+      $test_params     = $secondary_uri->getQueryParams();
+      $missing_params  = array_diff_key($good_params, $test_params);
+      $valid           = $valid_domain && empty($missing_params);
+    }
+    return $valid;
+  }
+
 }
diff --git a/src/applications/oauthserver/server/__init__.php b/src/applications/oauthserver/server/__init__.php
index 6fa671b012..f5951c33b1 100644
--- a/src/applications/oauthserver/server/__init__.php
+++ b/src/applications/oauthserver/server/__init__.php
@@ -1,18 +1,19 @@
 <?php
 /**
  * This file is automatically generated. Lint this module to rebuild it.
  * @generated
  */
 
 
 
 phutil_require_module('phabricator', 'applications/oauthserver/scope');
 phutil_require_module('phabricator', 'applications/oauthserver/storage/accesstoken');
 phutil_require_module('phabricator', 'applications/oauthserver/storage/authorizationcode');
 phutil_require_module('phabricator', 'applications/oauthserver/storage/clientauthorization');
+phutil_require_module('phabricator', 'infrastructure/env');
 
 phutil_require_module('phutil', 'filesystem');
 phutil_require_module('phutil', 'utils');
 
 
 phutil_require_source('PhabricatorOAuthServer.php');
diff --git a/src/applications/oauthserver/server/__tests__/PhabricatorOAuthServerTestCase.php b/src/applications/oauthserver/server/__tests__/PhabricatorOAuthServerTestCase.php
new file mode 100644
index 0000000000..fd195be301
--- /dev/null
+++ b/src/applications/oauthserver/server/__tests__/PhabricatorOAuthServerTestCase.php
@@ -0,0 +1,85 @@
+<?php
+
+/*
+ * Copyright 2012 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 PhabricatorOAuthServerTestCase
+  extends PhabricatorTestCase {
+
+  public function testValidateRedirectURI() {
+    static $map = array(
+      'http://www.google.com'              => true,
+      'http://www.google.com/'             => true,
+      'http://www.google.com/auth'         => true,
+      'www.google.com'                     => false,
+      'http://www.google.com/auth#invalid' => false
+    );
+    $server = new PhabricatorOAuthServer();
+    foreach ($map as $input => $expected) {
+      $uri = new PhutilURI($input);
+      $result = $server->validateRedirectURI($uri);
+      $this->assertEqual(
+        $expected,
+        $result,
+        "Validation of redirect URI '{$input}'"
+      );
+    }
+  }
+
+  public function testValidateSecondaryRedirectURI() {
+    $server      = new PhabricatorOAuthServer();
+    $primary_uri = new PhutilURI('http://www.google.com');
+    static $test_domain_map = array(
+      'http://www.google.com'               => true,
+      'http://www.google.com/'              => true,
+      'http://www.google.com/auth'          => true,
+      'http://www.google.com/?auth'         => true,
+      'www.google.com'                      => false,
+      'http://www.google.com/auth#invalid'  => false,
+      'http://www.example.com'              => false
+    );
+    foreach ($test_domain_map as $input => $expected) {
+      $uri = new PhutilURI($input);
+      $this->assertEqual(
+        $expected,
+        $server->validateSecondaryRedirectURI($uri, $primary_uri),
+        "Validation of redirect URI '{$input}' ".
+        "relative to '{$primary_uri}'"
+      );
+    }
+
+    $primary_uri = new PhutilURI('http://www.google.com/?auth');
+    static $test_query_map = array(
+      'http://www.google.com'               => false,
+      'http://www.google.com/'              => false,
+      'http://www.google.com/auth'          => false,
+      'http://www.google.com/?auth'         => true,
+      'http://www.google.com/?auth&stuff'   => true,
+      'http://www.google.com/?stuff'        => false,
+    );
+    foreach ($test_query_map as $input => $expected) {
+      $uri = new PhutilURI($input);
+      $this->assertEqual(
+        $expected,
+        $server->validateSecondaryRedirectURI($uri, $primary_uri),
+        "Validation of secondary redirect URI '{$input}' ".
+        "relative to '{$primary_uri}'"
+      );
+    }
+
+  }
+
+}
diff --git a/src/applications/oauthserver/server/__tests__/__init__.php b/src/applications/oauthserver/server/__tests__/__init__.php
new file mode 100644
index 0000000000..d5cbec1edf
--- /dev/null
+++ b/src/applications/oauthserver/server/__tests__/__init__.php
@@ -0,0 +1,15 @@
+<?php
+/**
+ * This file is automatically generated. Lint this module to rebuild it.
+ * @generated
+ */
+
+
+
+phutil_require_module('phabricator', 'applications/oauthserver/server');
+phutil_require_module('phabricator', 'infrastructure/testing/testcase');
+
+phutil_require_module('phutil', 'parser/uri');
+
+
+phutil_require_source('PhabricatorOAuthServerTestCase.php');
diff --git a/src/applications/oauthserver/storage/authorizationcode/PhabricatorOAuthServerAuthorizationCode.php b/src/applications/oauthserver/storage/authorizationcode/PhabricatorOAuthServerAuthorizationCode.php
index 1db429fac4..d9b67bef7b 100644
--- a/src/applications/oauthserver/storage/authorizationcode/PhabricatorOAuthServerAuthorizationCode.php
+++ b/src/applications/oauthserver/storage/authorizationcode/PhabricatorOAuthServerAuthorizationCode.php
@@ -1,30 +1,31 @@
 <?php
 
 /*
  * Copyright 2012 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.
  */
 
 /**
  * @group oauthserver
  */
 final class PhabricatorOAuthServerAuthorizationCode
 extends PhabricatorOAuthServerDAO {
 
   protected $id;
   protected $code;
   protected $clientPHID;
   protected $clientSecret;
   protected $userPHID;
+  protected $redirectURI;
 }
diff --git a/src/applications/oauthserver/storage/clientauthorization/PhabricatorOAuthClientAuthorization.php b/src/applications/oauthserver/storage/clientauthorization/PhabricatorOAuthClientAuthorization.php
index f5f627d279..435e84a631 100644
--- a/src/applications/oauthserver/storage/clientauthorization/PhabricatorOAuthClientAuthorization.php
+++ b/src/applications/oauthserver/storage/clientauthorization/PhabricatorOAuthClientAuthorization.php
@@ -1,59 +1,59 @@
 <?php
 
 /*
  * Copyright 2012 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.
  */
 
 /**
  * @group oauthserver
  */
 final class PhabricatorOAuthClientAuthorization
 extends PhabricatorOAuthServerDAO {
 
   protected $id;
   protected $phid;
   protected $userPHID;
   protected $clientPHID;
   protected $scope;
 
   public function getEditURI() {
     return '/oauthserver/clientauthorization/edit/'.$this->getPHID().'/';
   }
 
   public function getDeleteURI() {
     return '/oauthserver/clientauthorization/delete/'.$this->getPHID().'/';
   }
 
   public function getScopeString() {
     $scope = $this->getScope();
     $scopes = array_keys($scope);
     sort($scopes);
-    return implode(', ', $scopes);
+    return implode(' ', $scopes);
   }
 
   public function getConfiguration() {
     return array(
       self::CONFIG_AUX_PHID => true,
       self::CONFIG_SERIALIZATION => array(
         'scope' => self::SERIALIZATION_JSON,
       ),
     ) + parent::getConfiguration();
   }
 
   public function generatePHID() {
     return PhabricatorPHID::generateNewPHID(
       PhabricatorPHIDConstants::PHID_TYPE_OASA);
   }
 }