diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
index 05d4d64f4d..fadafe5e4e 100644
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -1,847 +1,851 @@
 <?php
 
 /**
  * This file is automatically generated. Use 'phutil_mapper.php' to rebuild it.
  * @generated
  */
 
 phutil_register_library_map(array(
   'class' =>
   array(
     'Aphront400Response' => 'aphront/response/400',
     'Aphront404Response' => 'aphront/response/404',
     'AphrontAjaxResponse' => 'aphront/response/ajax',
     'AphrontApplicationConfiguration' => 'aphront/applicationconfiguration',
     'AphrontController' => 'aphront/controller',
     'AphrontCrumbsView' => 'view/layout/crumbs',
     'AphrontDatabaseConnection' => 'storage/connection/base',
     'AphrontDefaultApplicationConfiguration' => 'aphront/default/configuration',
     'AphrontDefaultApplicationController' => 'aphront/default/controller',
     'AphrontDialogResponse' => 'aphront/response/dialog',
     'AphrontDialogView' => 'view/dialog',
     'AphrontErrorView' => 'view/form/error',
     'AphrontException' => 'aphront/exception/base',
     'AphrontFileResponse' => 'aphront/response/file',
     'AphrontFormCheckboxControl' => 'view/form/control/checkbox',
     'AphrontFormControl' => 'view/form/control/base',
     'AphrontFormDividerControl' => 'view/form/control/divider',
     'AphrontFormFileControl' => 'view/form/control/file',
     'AphrontFormMarkupControl' => 'view/form/control/markup',
     'AphrontFormPasswordControl' => 'view/form/control/password',
     'AphrontFormRecaptchaControl' => 'view/form/control/recaptcha',
     'AphrontFormSelectControl' => 'view/form/control/select',
     'AphrontFormStaticControl' => 'view/form/control/static',
     'AphrontFormSubmitControl' => 'view/form/control/submit',
     'AphrontFormTextAreaControl' => 'view/form/control/textarea',
     'AphrontFormTextControl' => 'view/form/control/text',
     'AphrontFormToggleButtonsControl' => 'view/form/control/togglebuttons',
     'AphrontFormTokenizerControl' => 'view/form/control/tokenizer',
     'AphrontFormView' => 'view/form/base',
     'AphrontHeadsupActionListView' => 'view/layout/headsup/actionlist',
     'AphrontHeadsupActionView' => 'view/layout/headsup/action',
     'AphrontIsolatedDatabaseConnection' => 'storage/connection/isolated',
     'AphrontIsolatedDatabaseConnectionTestCase' => 'storage/connection/isolated/__tests__',
     'AphrontListFilterView' => 'view/layout/listfilter',
     'AphrontMySQLDatabaseConnection' => 'storage/connection/mysql',
     'AphrontNullView' => 'view/null',
     'AphrontPageView' => 'view/page/base',
     'AphrontPagerView' => 'view/control/pager',
     'AphrontPanelView' => 'view/layout/panel',
     'AphrontQueryAccessDeniedException' => 'storage/exception/accessdenied',
     'AphrontQueryConnectionException' => 'storage/exception/connection',
     'AphrontQueryConnectionLostException' => 'storage/exception/connectionlost',
     'AphrontQueryCountException' => 'storage/exception/count',
     'AphrontQueryDuplicateKeyException' => 'storage/exception/duplicatekey',
     'AphrontQueryException' => 'storage/exception/base',
     'AphrontQueryObjectMissingException' => 'storage/exception/objectmissing',
     'AphrontQueryParameterException' => 'storage/exception/parameter',
     'AphrontQueryRecoverableException' => 'storage/exception/recoverable',
     'AphrontRedirectException' => 'aphront/exception/redirect',
     'AphrontRedirectResponse' => 'aphront/response/redirect',
     'AphrontRequest' => 'aphront/request',
     'AphrontRequestFailureView' => 'view/page/failure',
     'AphrontResponse' => 'aphront/response/base',
     'AphrontSideNavView' => 'view/layout/sidenav',
     'AphrontTableView' => 'view/control/table',
     'AphrontTokenizerTemplateView' => 'view/control/tokenizer',
     'AphrontTypeaheadTemplateView' => 'view/control/typeahead',
     'AphrontURIMapper' => 'aphront/mapper',
     'AphrontView' => 'view/base',
     'AphrontWebpageResponse' => 'aphront/response/webpage',
     'CelerityAPI' => 'infrastructure/celerity/api',
     'CelerityResourceController' => 'infrastructure/celerity/controller',
     'CelerityResourceMap' => 'infrastructure/celerity/map',
     'CelerityStaticResourceResponse' => 'infrastructure/celerity/response',
     'ConduitAPIMethod' => 'applications/conduit/method/base',
     'ConduitAPIRequest' => 'applications/conduit/protocol/request',
     'ConduitAPI_conduit_connect_Method' => 'applications/conduit/method/conduit/connect',
     'ConduitAPI_conduit_ping_Method' => 'applications/conduit/method/conduit/ping',
     'ConduitAPI_daemon_launched_Method' => 'applications/conduit/method/daemon/launched',
     'ConduitAPI_daemon_log_Method' => 'applications/conduit/method/daemon/log',
     'ConduitAPI_differential_creatediff_Method' => 'applications/conduit/method/differential/creatediff',
     'ConduitAPI_differential_createrevision_Method' => 'applications/conduit/method/differential/createrevision',
     'ConduitAPI_differential_find_Method' => 'applications/conduit/method/differential/find',
     'ConduitAPI_differential_getalldiffs_Method' => 'applications/conduit/method/differential/getalldiffs',
     'ConduitAPI_differential_getcommitmessage_Method' => 'applications/conduit/method/differential/getcommitmessage',
     'ConduitAPI_differential_getcommitpaths_Method' => 'applications/conduit/method/differential/getcommitpaths',
     'ConduitAPI_differential_getdiff_Method' => 'applications/conduit/method/differential/getdiff',
     'ConduitAPI_differential_getrevision_Method' => 'applications/conduit/method/differential/getrevision',
     'ConduitAPI_differential_getrevisionfeedback_Method' => 'applications/conduit/method/differential/getrevisionfeedback',
     'ConduitAPI_differential_markcommitted_Method' => 'applications/conduit/method/differential/markcommitted',
     'ConduitAPI_differential_parsecommitmessage_Method' => 'applications/conduit/method/differential/parsecommitmessage',
     'ConduitAPI_differential_setdiffproperty_Method' => 'applications/conduit/method/differential/setdiffproperty',
     'ConduitAPI_differential_updaterevision_Method' => 'applications/conduit/method/differential/updaterevision',
     'ConduitAPI_differential_updatetaskrevisionassoc_Method' => 'applications/conduit/method/differential/updatetaskrevisionassoc',
     'ConduitAPI_diffusion_getcommits_Method' => 'applications/conduit/method/diffusion/getcommits',
     'ConduitAPI_file_upload_Method' => 'applications/conduit/method/file/upload',
     'ConduitAPI_path_getowners_Method' => 'applications/conduit/method/path/getowners',
     'ConduitAPI_user_find_Method' => 'applications/conduit/method/user/find',
     'ConduitAPI_user_whoami_Method' => 'applications/conduit/method/user/whoami',
     'ConduitException' => 'applications/conduit/protocol/exception',
     'DarkConsole' => 'aphront/console/api',
     'DarkConsoleConfigPlugin' => 'aphront/console/plugin/config',
     'DarkConsoleController' => 'aphront/console/controller',
     'DarkConsoleCore' => 'aphront/console/core',
     'DarkConsoleErrorLogPlugin' => 'aphront/console/plugin/errorlog',
     'DarkConsoleErrorLogPluginAPI' => 'aphront/console/plugin/errorlog/api',
     'DarkConsolePlugin' => 'aphront/console/plugin/base',
     'DarkConsoleRequestPlugin' => 'aphront/console/plugin/request',
     'DarkConsoleServicesPlugin' => 'aphront/console/plugin/services',
     'DarkConsoleServicesPluginAPI' => 'aphront/console/plugin/services/api',
     'DarkConsoleXHProfPlugin' => 'aphront/console/plugin/xhprof',
     'DarkConsoleXHProfPluginAPI' => 'aphront/console/plugin/xhprof/api',
     'DatabaseConfigurationProvider' => 'applications/base/storage/configuration',
     'DifferentialAction' => 'applications/differential/constants/action',
     'DifferentialAddCommentView' => 'applications/differential/view/addcomment',
     'DifferentialAttachController' => 'applications/differential/controller/attach',
     'DifferentialCCWelcomeMail' => 'applications/differential/mail/ccwelcome',
     'DifferentialChangeType' => 'applications/differential/constants/changetype',
     'DifferentialChangeset' => 'applications/differential/storage/changeset',
     'DifferentialChangesetDetailView' => 'applications/differential/view/changesetdetailview',
     'DifferentialChangesetListView' => 'applications/differential/view/changesetlistview',
     'DifferentialChangesetParser' => 'applications/differential/parser/changeset',
     'DifferentialChangesetViewController' => 'applications/differential/controller/changesetview',
     'DifferentialComment' => 'applications/differential/storage/comment',
     'DifferentialCommentEditor' => 'applications/differential/editor/comment',
     'DifferentialCommentMail' => 'applications/differential/mail/comment',
     'DifferentialCommentPreviewController' => 'applications/differential/controller/commentpreview',
     'DifferentialCommentSaveController' => 'applications/differential/controller/commentsave',
     'DifferentialCommitMessage' => 'applications/differential/parser/commitmessage',
     'DifferentialCommitMessageData' => 'applications/differential/data/commitmessage',
     'DifferentialCommitMessageParserException' => 'applications/differential/parser/commitmessage/exception',
     'DifferentialController' => 'applications/differential/controller/base',
     'DifferentialDAO' => 'applications/differential/storage/base',
     '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',
     'DifferentialHunk' => 'applications/differential/storage/hunk',
     'DifferentialInlineComment' => 'applications/differential/storage/inlinecomment',
     'DifferentialInlineCommentEditController' => 'applications/differential/controller/inlinecommentedit',
     'DifferentialInlineCommentPreviewController' => 'applications/differential/controller/inlinecommentpreview',
     'DifferentialInlineCommentView' => 'applications/differential/view/inlinecomment',
     'DifferentialLintStatus' => 'applications/differential/constants/lintstatus',
     'DifferentialMail' => 'applications/differential/mail/base',
     'DifferentialMarkupEngineFactory' => 'applications/differential/parser/markup',
     'DifferentialNewDiffMail' => 'applications/differential/mail/newdiff',
     'DifferentialReviewRequestMail' => 'applications/differential/mail/reviewrequest',
     'DifferentialRevision' => 'applications/differential/storage/revision',
     'DifferentialRevisionCommentListView' => 'applications/differential/view/revisioncommentlist',
     'DifferentialRevisionCommentView' => 'applications/differential/view/revisioncomment',
     'DifferentialRevisionControlSystem' => 'applications/differential/constants/revisioncontrolsystem',
     'DifferentialRevisionDetailRenderer' => 'applications/differential/controller/customrenderer',
     'DifferentialRevisionDetailView' => 'applications/differential/view/revisiondetail',
     'DifferentialRevisionEditController' => 'applications/differential/controller/revisionedit',
     'DifferentialRevisionEditor' => 'applications/differential/editor/revision',
     'DifferentialRevisionListController' => 'applications/differential/controller/revisionlist',
     'DifferentialRevisionListData' => 'applications/differential/data/revisionlist',
     'DifferentialRevisionStatus' => 'applications/differential/constants/revisionstatus',
     'DifferentialRevisionUpdateHistoryView' => 'applications/differential/view/revisionupdatehistory',
     'DifferentialRevisionViewController' => 'applications/differential/controller/revisionview',
     'DifferentialSubscribeController' => 'applications/differential/controller/subscribe',
     'DifferentialTasksAttacher' => 'applications/differential/tasks',
     'DifferentialUnitStatus' => 'applications/differential/constants/unitstatus',
     'DifferentialViewTime' => 'applications/differential/storage/viewtime',
     'DiffusionBranchInformation' => 'applications/diffusion/data/branch',
     'DiffusionBranchQuery' => 'applications/diffusion/query/branch/base',
     'DiffusionBranchTableView' => 'applications/diffusion/view/branchtable',
     'DiffusionBrowseController' => 'applications/diffusion/controller/browse',
     'DiffusionBrowseFileController' => 'applications/diffusion/controller/file',
     'DiffusionBrowseQuery' => 'applications/diffusion/query/browse/base',
     'DiffusionBrowseTableView' => 'applications/diffusion/view/browsetable',
     'DiffusionChangeController' => 'applications/diffusion/controller/change',
     'DiffusionCommitChangeTableView' => 'applications/diffusion/view/commitchangetable',
     'DiffusionCommitController' => 'applications/diffusion/controller/commit',
     'DiffusionController' => 'applications/diffusion/controller/base',
     'DiffusionDiffController' => 'applications/diffusion/controller/diff',
     'DiffusionDiffQuery' => 'applications/diffusion/query/diff/base',
     'DiffusionFileContent' => 'applications/diffusion/data/filecontent',
     'DiffusionFileContentQuery' => 'applications/diffusion/query/filecontent/base',
     'DiffusionGitBranchQuery' => 'applications/diffusion/query/branch/git',
     'DiffusionGitBrowseQuery' => 'applications/diffusion/query/browse/git',
     'DiffusionGitDiffQuery' => 'applications/diffusion/query/diff/git',
     'DiffusionGitFileContentQuery' => 'applications/diffusion/query/filecontent/git',
     'DiffusionGitHistoryQuery' => 'applications/diffusion/query/history/git',
     'DiffusionGitLastModifiedQuery' => 'applications/diffusion/query/lastmodified/git',
     'DiffusionGitPathIDQuery' => 'applications/diffusion/query/pathid/base',
     'DiffusionGitRequest' => 'applications/diffusion/request/git',
     'DiffusionHistoryController' => 'applications/diffusion/controller/history',
     'DiffusionHistoryQuery' => 'applications/diffusion/query/history/base',
     'DiffusionHistoryTableView' => 'applications/diffusion/view/historytable',
     'DiffusionHomeController' => 'applications/diffusion/controller/home',
     'DiffusionLastModifiedController' => 'applications/diffusion/controller/lastmodified',
     'DiffusionLastModifiedQuery' => 'applications/diffusion/query/lastmodified/base',
     'DiffusionPathChange' => 'applications/diffusion/data/pathchange',
     'DiffusionPathChangeQuery' => 'applications/diffusion/query/pathchange/base',
     'DiffusionPathCompleteController' => 'applications/diffusion/controller/pathcomplete',
     'DiffusionPathValidateController' => 'applications/diffusion/controller/pathvalidate',
     'DiffusionRepositoryController' => 'applications/diffusion/controller/repository',
     'DiffusionRepositoryPath' => 'applications/diffusion/data/repositorypath',
     'DiffusionRequest' => 'applications/diffusion/request/base',
     'DiffusionSvnBrowseQuery' => 'applications/diffusion/query/browse/svn',
     'DiffusionSvnDiffQuery' => 'applications/diffusion/query/diff/svn',
     'DiffusionSvnFileContentQuery' => 'applications/diffusion/query/filecontent/svn',
     'DiffusionSvnHistoryQuery' => 'applications/diffusion/query/history/svn',
     'DiffusionSvnLastModifiedQuery' => 'applications/diffusion/query/lastmodified/svn',
     'DiffusionSvnRequest' => 'applications/diffusion/request/svn',
     'DiffusionView' => 'applications/diffusion/view/base',
     'HeraldAction' => 'applications/herald/storage/action',
     'HeraldActionConfig' => 'applications/herald/config/action',
     'HeraldApplyTranscript' => 'applications/herald/storage/transcript/apply',
     'HeraldCommitAdapter' => 'applications/herald/adapter/commit',
     'HeraldCondition' => 'applications/herald/storage/condition',
     'HeraldConditionConfig' => 'applications/herald/config/condition',
     'HeraldConditionTranscript' => 'applications/herald/storage/transcript/condition',
     'HeraldContentTypeConfig' => 'applications/herald/config/contenttype',
     'HeraldController' => 'applications/herald/controller/base',
     'HeraldDAO' => 'applications/herald/storage/base',
     'HeraldDeleteController' => 'applications/herald/controller/delete',
     'HeraldDifferentialRevisionAdapter' => 'applications/herald/adapter/differential',
     'HeraldDryRunAdapter' => 'applications/herald/adapter/dryrun',
     'HeraldEffect' => 'applications/herald/engine/effect',
     'HeraldEngine' => 'applications/herald/engine/engine',
     'HeraldFieldConfig' => 'applications/herald/config/field',
     'HeraldHomeController' => 'applications/herald/controller/home',
     'HeraldInvalidConditionException' => 'applications/herald/engine/engine/exception',
     'HeraldInvalidFieldException' => 'applications/herald/engine/engine/exception',
     'HeraldNewController' => 'applications/herald/controller/new',
     'HeraldObjectAdapter' => 'applications/herald/adapter/base',
     'HeraldObjectTranscript' => 'applications/herald/storage/transcript/object',
     'HeraldRecursiveConditionsException' => 'applications/herald/engine/engine/exception',
     'HeraldRule' => 'applications/herald/storage/rule',
     'HeraldRuleController' => 'applications/herald/controller/rule',
     'HeraldRuleTranscript' => 'applications/herald/storage/transcript/rule',
     'HeraldTestConsoleController' => 'applications/herald/controller/test',
     'HeraldTranscript' => 'applications/herald/storage/transcript/base',
     'HeraldTranscriptController' => 'applications/herald/controller/transcript',
     'HeraldTranscriptListController' => 'applications/herald/controller/transcriptlist',
     'HeraldValueTypeConfig' => 'applications/herald/config/valuetype',
     'Javelin' => 'infrastructure/javelin/api',
     'LiskDAO' => 'storage/lisk/dao',
     'LiskIsolationTestCase' => 'storage/lisk/dao/__tests__',
     'LiskIsolationTestDAO' => 'storage/lisk/dao/__tests__',
     'LiskIsolationTestDAOException' => 'storage/lisk/dao/__tests__',
     'ManiphestController' => 'applications/maniphest/controller/base',
     'ManiphestDAO' => 'applications/maniphest/storage/base',
     'ManiphestTask' => 'applications/maniphest/storage/task',
     'ManiphestTaskDetailController' => 'applications/maniphest/controller/taskdetail',
     'ManiphestTaskEditController' => 'applications/maniphest/controller/taskedit',
     'ManiphestTaskListController' => 'applications/maniphest/controller/tasklist',
     'ManiphestTaskListView' => 'applications/maniphest/view/tasklist',
     'ManiphestTaskPriority' => 'applications/maniphest/constants/priority',
     'ManiphestTaskSelectorSearchController' => 'applications/maniphest/controller/taskselectorsearch',
     'ManiphestTaskStatus' => 'applications/maniphest/constants/status',
     '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',
     'ManiphestTransactionSaveController' => 'applications/maniphest/controller/transactionsave',
     'ManiphestTransactionType' => 'applications/maniphest/constants/transactiontype',
     'Phabricator404Controller' => 'applications/base/controller/404',
     'PhabricatorAuthController' => 'applications/auth/controller/base',
     'PhabricatorConduitAPIController' => 'applications/conduit/controller/api',
     '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',
     'PhabricatorController' => 'applications/base/controller/base',
     'PhabricatorDaemon' => 'infrastructure/daemon/base',
     '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',
     'PhabricatorDaemonLogViewController' => 'applications/daemon/controller/logview',
     'PhabricatorDaemonReference' => 'infrastructure/daemon/control/reference',
     'PhabricatorDaemonTimelineConsoleController' => 'applications/daemon/controller/timeline',
     'PhabricatorDaemonTimelineEventController' => 'applications/daemon/controller/timelineevent',
     'PhabricatorDirectoryCategory' => 'applications/directory/storage/category',
     'PhabricatorDirectoryCategoryDeleteController' => 'applications/directory/controller/categorydelete',
     'PhabricatorDirectoryCategoryEditController' => 'applications/directory/controller/categoryedit',
     'PhabricatorDirectoryCategoryListController' => 'applications/directory/controller/categorylist',
     'PhabricatorDirectoryController' => 'applications/directory/controller/base',
     'PhabricatorDirectoryDAO' => 'applications/directory/storage/base',
     'PhabricatorDirectoryItem' => 'applications/directory/storage/item',
     'PhabricatorDirectoryItemDeleteController' => 'applications/directory/controller/itemdelete',
     'PhabricatorDirectoryItemEditController' => 'applications/directory/controller/itemedit',
     'PhabricatorDirectoryItemListController' => 'applications/directory/controller/itemlist',
     'PhabricatorDirectoryMainController' => 'applications/directory/controller/main',
     'PhabricatorDraft' => 'applications/draft/storage/draft',
     'PhabricatorDraftDAO' => 'applications/draft/storage/base',
     'PhabricatorEditPreferencesController' => 'applications/preferences/controller/edit',
     'PhabricatorEmailLoginController' => 'applications/auth/controller/email',
     'PhabricatorEmailTokenController' => 'applications/auth/controller/emailtoken',
     'PhabricatorEnv' => 'infrastructure/env',
     'PhabricatorFile' => 'applications/files/storage/file',
     'PhabricatorFileController' => 'applications/files/controller/base',
     'PhabricatorFileDAO' => 'applications/files/storage/base',
     'PhabricatorFileImageMacro' => 'applications/files/storage/imagemacro',
     'PhabricatorFileListController' => 'applications/files/controller/list',
     'PhabricatorFileStorageBlob' => 'applications/files/storage/storageblob',
     'PhabricatorFileURI' => 'applications/files/uri',
     'PhabricatorFileUploadController' => 'applications/files/controller/upload',
     'PhabricatorFileViewController' => 'applications/files/controller/view',
     'PhabricatorGoodForNothingWorker' => 'infrastructure/daemon/workers/worker/goodfornothing',
     'PhabricatorHandleObjectSelectorDataView' => 'applications/phid/handle/view/selector',
     'PhabricatorLiskDAO' => 'applications/base/storage/lisk',
     'PhabricatorLoginController' => 'applications/auth/controller/login',
     'PhabricatorLogoutController' => 'applications/auth/controller/logout',
     'PhabricatorMailImplementationAdapter' => 'applications/metamta/adapter/base',
     'PhabricatorMailImplementationAmazonSESAdapter' => 'applications/metamta/adapter/amazonses',
     'PhabricatorMailImplementationPHPMailerLiteAdapter' => 'applications/metamta/adapter/phpmailerlite',
+    'PhabricatorMailImplementationTestAdapter' => 'applications/metamta/adapter/test',
     'PhabricatorMetaMTAController' => 'applications/metamta/controller/base',
     'PhabricatorMetaMTADAO' => 'applications/metamta/storage/base',
     'PhabricatorMetaMTADaemon' => 'applications/metamta/daemon/mta',
     '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',
     'PhabricatorMetaMTASendController' => 'applications/metamta/controller/send',
     'PhabricatorMetaMTAViewController' => 'applications/metamta/controller/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',
     'PhabricatorOAuthRegistrationController' => 'applications/auth/controller/oauthregistration/base',
     'PhabricatorOAuthUnlinkController' => 'applications/auth/controller/unlink',
     'PhabricatorObjectHandle' => 'applications/phid/handle',
     'PhabricatorObjectHandleData' => 'applications/phid/handle/data',
     'PhabricatorObjectSelectorDialog' => 'view/control/objectselector',
     'PhabricatorOwnersController' => 'applications/owners/controller/base',
     'PhabricatorOwnersDAO' => 'applications/owners/storage/base',
     'PhabricatorOwnersDeleteController' => 'applications/owners/controller/delete',
     'PhabricatorOwnersDetailController' => 'applications/owners/controller/detail',
     'PhabricatorOwnersEditController' => 'applications/owners/controller/edit',
     'PhabricatorOwnersListController' => 'applications/owners/controller/list',
     'PhabricatorOwnersOwner' => 'applications/owners/storage/owner',
     'PhabricatorOwnersPackage' => 'applications/owners/storage/package',
     'PhabricatorOwnersPath' => 'applications/owners/storage/path',
     'PhabricatorPHID' => 'applications/phid/storage/phid',
     'PhabricatorPHIDAllocateController' => 'applications/phid/controller/allocate',
     'PhabricatorPHIDConstants' => 'applications/phid/constants',
     'PhabricatorPHIDController' => 'applications/phid/controller/base',
     'PhabricatorPHIDDAO' => 'applications/phid/storage/base',
     'PhabricatorPHIDListController' => 'applications/phid/controller/list',
     'PhabricatorPHIDLookupController' => 'applications/phid/controller/lookup',
     'PhabricatorPeopleController' => 'applications/people/controller/base',
     'PhabricatorPeopleEditController' => 'applications/people/controller/edit',
     'PhabricatorPeopleListController' => 'applications/people/controller/list',
     'PhabricatorPeopleProfileController' => 'applications/people/controller/profile',
     'PhabricatorPeopleProfileEditController' => 'applications/people/controller/profileedit',
     'PhabricatorPreferencesController' => 'applications/preferences/controller/base',
     'PhabricatorProject' => 'applications/project/storage/project',
     'PhabricatorProjectAffiliation' => 'applications/project/storage/affiliation',
     'PhabricatorProjectAffiliationEditController' => 'applications/project/controller/editaffiliation',
     'PhabricatorProjectController' => 'applications/project/controller/base',
     'PhabricatorProjectDAO' => 'applications/project/storage/base',
     'PhabricatorProjectEditController' => 'applications/project/controller/edit',
     'PhabricatorProjectListController' => 'applications/project/controller/list',
     'PhabricatorProjectProfile' => 'applications/project/storage/profile',
     'PhabricatorProjectProfileController' => 'applications/project/controller/profile',
     'PhabricatorRedirectController' => 'applications/base/controller/redirect',
     'PhabricatorRemarkupRuleDifferential' => 'infrastructure/markup/remarkup/markuprule/differential',
     'PhabricatorRemarkupRuleDiffusion' => 'infrastructure/markup/remarkup/markuprule/diffusion',
     'PhabricatorRemarkupRuleImageMacro' => 'infrastructure/markup/remarkup/markuprule/imagemacro',
     'PhabricatorRemarkupRuleManiphest' => 'infrastructure/markup/remarkup/markuprule/maniphest',
     'PhabricatorRepository' => 'applications/repository/storage/repository',
     'PhabricatorRepositoryArcanistProject' => 'applications/repository/storage/arcanistproject',
     'PhabricatorRepositoryArcanistProjectEditController' => 'applications/repository/controller/arcansistprojectedit',
     'PhabricatorRepositoryCommit' => 'applications/repository/storage/commit',
     'PhabricatorRepositoryCommitChangeParserWorker' => 'applications/repository/worker/commitchangeparser/base',
     'PhabricatorRepositoryCommitData' => 'applications/repository/storage/commitdata',
     'PhabricatorRepositoryCommitDiscoveryDaemon' => 'applications/repository/daemon/commitdiscovery/base',
     'PhabricatorRepositoryCommitHeraldWorker' => 'applications/repository/worker/herald',
     'PhabricatorRepositoryCommitMessageDetailParser' => 'applications/repository/parser/base',
     'PhabricatorRepositoryCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/base',
     'PhabricatorRepositoryCommitParserWorker' => 'applications/repository/worker/base',
     'PhabricatorRepositoryCommitTaskDaemon' => 'applications/repository/daemon/committask',
     'PhabricatorRepositoryController' => 'applications/repository/controller/base',
     'PhabricatorRepositoryCreateController' => 'applications/repository/controller/create',
     'PhabricatorRepositoryDAO' => 'applications/repository/storage/base',
     'PhabricatorRepositoryDaemon' => 'applications/repository/daemon/base',
     'PhabricatorRepositoryDefaultCommitMessageDetailParser' => 'applications/repository/parser/default',
     'PhabricatorRepositoryEditController' => 'applications/repository/controller/edit',
     'PhabricatorRepositoryGitCommitChangeParserWorker' => 'applications/repository/worker/commitchangeparser/git',
     'PhabricatorRepositoryGitCommitDiscoveryDaemon' => 'applications/repository/daemon/commitdiscovery/git',
     'PhabricatorRepositoryGitCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/git',
     'PhabricatorRepositoryGitFetchDaemon' => 'applications/repository/daemon/gitfetch',
     'PhabricatorRepositoryGitHubNotification' => 'applications/repository/storage/githubnotification',
     'PhabricatorRepositoryGitHubPostReceiveController' => 'applications/repository/controller/github-post-receive',
     'PhabricatorRepositoryListController' => 'applications/repository/controller/list',
     'PhabricatorRepositoryShortcut' => 'applications/repository/storage/shortcut',
     'PhabricatorRepositorySvnCommitChangeParserWorker' => 'applications/repository/worker/commitchangeparser/svn',
     'PhabricatorRepositorySvnCommitDiscoveryDaemon' => 'applications/repository/daemon/commitdiscovery/svn',
     'PhabricatorRepositorySvnCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/svn',
     'PhabricatorRepositoryType' => 'applications/repository/constants/repositorytype',
     'PhabricatorSearchAbstractDocument' => 'applications/search/index/abstractdocument',
     'PhabricatorSearchBaseController' => 'applications/search/controller/base',
     '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',
     'PhabricatorSearchExecutor' => 'applications/search/execute/base',
     'PhabricatorSearchField' => 'applications/search/constants/field',
     'PhabricatorSearchManiphestIndexer' => 'applications/search/index/indexer/maniphest',
     'PhabricatorSearchMySQLExecutor' => 'applications/search/execute/mysql',
     'PhabricatorSearchQuery' => 'applications/search/storage/query',
     'PhabricatorSearchRelationship' => 'applications/search/constants/relationship',
     'PhabricatorStandardPageView' => 'view/page/standard',
     'PhabricatorStatusController' => 'applications/status/base',
     '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',
     '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',
     'PhabricatorUserDAO' => 'applications/people/storage/base',
     'PhabricatorUserOAuthInfo' => 'applications/people/storage/useroauthinfo',
     'PhabricatorUserPreferences' => 'applications/people/storage/preferences',
     'PhabricatorUserProfile' => 'applications/people/storage/profile',
     'PhabricatorUserSettingsController' => 'applications/people/controller/settings',
     'PhabricatorWorker' => 'infrastructure/daemon/workers/worker',
     'PhabricatorWorkerDAO' => 'infrastructure/daemon/workers/storage/base',
     'PhabricatorWorkerTask' => 'infrastructure/daemon/workers/storage/task',
     'PhabricatorWorkerTaskData' => 'infrastructure/daemon/workers/storage/taskdata',
     'PhabricatorWorkerTaskDetailController' => 'applications/daemon/controller/workertaskdetail',
     'PhabricatorXHPASTViewController' => 'applications/xhpastview/controller/base',
     'PhabricatorXHPASTViewDAO' => 'applications/xhpastview/storage/base',
     'PhabricatorXHPASTViewFrameController' => 'applications/xhpastview/controller/viewframe',
     'PhabricatorXHPASTViewFramesetController' => 'applications/xhpastview/controller/viewframeset',
     'PhabricatorXHPASTViewInputController' => 'applications/xhpastview/controller/viewinput',
     'PhabricatorXHPASTViewPanelController' => 'applications/xhpastview/controller/viewpanel',
     'PhabricatorXHPASTViewParseTree' => 'applications/xhpastview/storage/parsetree',
     'PhabricatorXHPASTViewRunController' => 'applications/xhpastview/controller/run',
     'PhabricatorXHPASTViewStreamController' => 'applications/xhpastview/controller/viewstream',
     'PhabricatorXHPASTViewTreeController' => 'applications/xhpastview/controller/viewtree',
     'PhabricatorXHProfController' => 'applications/xhprof/controller/base',
     'PhabricatorXHProfProfileController' => 'applications/xhprof/controller/profile',
     'PhabricatorXHProfProfileSymbolView' => 'applications/xhprof/view/symbol',
     'PhabricatorXHProfProfileTopLevelView' => 'applications/xhprof/view/toplevel',
   ),
   'function' =>
   array(
     '_qsprintf_check_scalar_type' => 'storage/qsprintf',
     '_qsprintf_check_type' => 'storage/qsprintf',
     'celerity_generate_unique_node_id' => 'infrastructure/celerity/api',
     'celerity_register_resource_map' => 'infrastructure/celerity/map',
     'javelin_render_tag' => 'infrastructure/javelin/markup',
     'phabricator_format_relative_time' => 'view/utils',
     'phabricator_format_timestamp' => 'view/utils',
     'phabricator_format_units_generic' => 'view/utils',
     'phabricator_render_form' => 'infrastructure/javelin/markup',
     '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(
     'Aphront400Response' => 'AphrontResponse',
     'Aphront404Response' => 'AphrontResponse',
     'AphrontAjaxResponse' => 'AphrontResponse',
     'AphrontCrumbsView' => 'AphrontView',
     'AphrontDefaultApplicationConfiguration' => 'AphrontApplicationConfiguration',
     'AphrontDefaultApplicationController' => 'AphrontController',
     'AphrontDialogResponse' => 'AphrontResponse',
     'AphrontDialogView' => 'AphrontView',
     'AphrontErrorView' => 'AphrontView',
     'AphrontFileResponse' => 'AphrontResponse',
     'AphrontFormCheckboxControl' => 'AphrontFormControl',
     'AphrontFormControl' => 'AphrontView',
     'AphrontFormDividerControl' => 'AphrontFormControl',
     'AphrontFormFileControl' => 'AphrontFormControl',
     'AphrontFormMarkupControl' => 'AphrontFormControl',
     'AphrontFormPasswordControl' => 'AphrontFormControl',
     'AphrontFormRecaptchaControl' => 'AphrontFormControl',
     'AphrontFormSelectControl' => 'AphrontFormControl',
     'AphrontFormStaticControl' => 'AphrontFormControl',
     'AphrontFormSubmitControl' => 'AphrontFormControl',
     'AphrontFormTextAreaControl' => 'AphrontFormControl',
     'AphrontFormTextControl' => 'AphrontFormControl',
     'AphrontFormToggleButtonsControl' => 'AphrontFormControl',
     'AphrontFormTokenizerControl' => 'AphrontFormControl',
     'AphrontFormView' => 'AphrontView',
     'AphrontHeadsupActionListView' => 'AphrontView',
     'AphrontHeadsupActionView' => 'AphrontView',
     'AphrontIsolatedDatabaseConnection' => 'AphrontDatabaseConnection',
     'AphrontIsolatedDatabaseConnectionTestCase' => 'PhabricatorTestCase',
     'AphrontListFilterView' => 'AphrontView',
     'AphrontMySQLDatabaseConnection' => 'AphrontDatabaseConnection',
     'AphrontNullView' => 'AphrontView',
     'AphrontPageView' => 'AphrontView',
     'AphrontPagerView' => 'AphrontView',
     'AphrontPanelView' => 'AphrontView',
     'AphrontQueryAccessDeniedException' => 'AphrontQueryRecoverableException',
     'AphrontQueryConnectionException' => 'AphrontQueryException',
     'AphrontQueryConnectionLostException' => 'AphrontQueryRecoverableException',
     'AphrontQueryCountException' => 'AphrontQueryException',
     'AphrontQueryDuplicateKeyException' => 'AphrontQueryException',
     'AphrontQueryObjectMissingException' => 'AphrontQueryException',
     'AphrontQueryParameterException' => 'AphrontQueryException',
     'AphrontQueryRecoverableException' => 'AphrontQueryException',
     'AphrontRedirectException' => 'AphrontException',
     'AphrontRedirectResponse' => 'AphrontResponse',
     'AphrontRequestFailureView' => 'AphrontView',
     'AphrontSideNavView' => 'AphrontView',
     'AphrontTableView' => 'AphrontView',
     'AphrontTokenizerTemplateView' => 'AphrontView',
     'AphrontTypeaheadTemplateView' => 'AphrontView',
     'AphrontWebpageResponse' => 'AphrontResponse',
     'CelerityResourceController' => 'AphrontController',
     'ConduitAPI_conduit_connect_Method' => 'ConduitAPIMethod',
     'ConduitAPI_conduit_ping_Method' => 'ConduitAPIMethod',
     'ConduitAPI_daemon_launched_Method' => 'ConduitAPIMethod',
     'ConduitAPI_daemon_log_Method' => 'ConduitAPIMethod',
     'ConduitAPI_differential_creatediff_Method' => 'ConduitAPIMethod',
     'ConduitAPI_differential_createrevision_Method' => 'ConduitAPIMethod',
     'ConduitAPI_differential_find_Method' => 'ConduitAPIMethod',
     'ConduitAPI_differential_getalldiffs_Method' => 'ConduitAPIMethod',
     'ConduitAPI_differential_getcommitmessage_Method' => 'ConduitAPIMethod',
     'ConduitAPI_differential_getcommitpaths_Method' => 'ConduitAPIMethod',
     'ConduitAPI_differential_getdiff_Method' => 'ConduitAPIMethod',
     'ConduitAPI_differential_getrevision_Method' => 'ConduitAPIMethod',
     'ConduitAPI_differential_getrevisionfeedback_Method' => 'ConduitAPIMethod',
     'ConduitAPI_differential_markcommitted_Method' => 'ConduitAPIMethod',
     'ConduitAPI_differential_parsecommitmessage_Method' => 'ConduitAPIMethod',
     'ConduitAPI_differential_setdiffproperty_Method' => 'ConduitAPIMethod',
     'ConduitAPI_differential_updaterevision_Method' => 'ConduitAPIMethod',
     'ConduitAPI_differential_updatetaskrevisionassoc_Method' => 'ConduitAPIMethod',
     'ConduitAPI_diffusion_getcommits_Method' => 'ConduitAPIMethod',
     'ConduitAPI_file_upload_Method' => 'ConduitAPIMethod',
     'ConduitAPI_path_getowners_Method' => 'ConduitAPIMethod',
     'ConduitAPI_user_find_Method' => 'ConduitAPIMethod',
     'ConduitAPI_user_whoami_Method' => 'ConduitAPIMethod',
     'DarkConsoleConfigPlugin' => 'DarkConsolePlugin',
     'DarkConsoleController' => 'PhabricatorController',
     'DarkConsoleErrorLogPlugin' => 'DarkConsolePlugin',
     'DarkConsoleRequestPlugin' => 'DarkConsolePlugin',
     'DarkConsoleServicesPlugin' => 'DarkConsolePlugin',
     'DarkConsoleXHProfPlugin' => 'DarkConsolePlugin',
     'DifferentialAddCommentView' => 'AphrontView',
     'DifferentialAttachController' => 'DifferentialController',
     'DifferentialCCWelcomeMail' => 'DifferentialReviewRequestMail',
     'DifferentialChangeset' => 'DifferentialDAO',
     'DifferentialChangesetDetailView' => 'AphrontView',
     'DifferentialChangesetListView' => 'AphrontView',
     'DifferentialChangesetViewController' => 'DifferentialController',
     'DifferentialComment' => 'DifferentialDAO',
     'DifferentialCommentMail' => 'DifferentialMail',
     'DifferentialCommentPreviewController' => 'DifferentialController',
     'DifferentialCommentSaveController' => 'DifferentialController',
     'DifferentialController' => 'PhabricatorController',
     'DifferentialDAO' => 'PhabricatorLiskDAO',
     'DifferentialDiff' => 'DifferentialDAO',
     'DifferentialDiffContentMail' => 'DifferentialMail',
     'DifferentialDiffCreateController' => 'DifferentialController',
     'DifferentialDiffProperty' => 'DifferentialDAO',
     'DifferentialDiffTableOfContentsView' => 'AphrontView',
     'DifferentialDiffViewController' => 'DifferentialController',
     'DifferentialHunk' => 'DifferentialDAO',
     'DifferentialInlineComment' => 'DifferentialDAO',
     'DifferentialInlineCommentEditController' => 'DifferentialController',
     'DifferentialInlineCommentPreviewController' => 'DifferentialController',
     'DifferentialInlineCommentView' => 'AphrontView',
     'DifferentialNewDiffMail' => 'DifferentialReviewRequestMail',
     'DifferentialReviewRequestMail' => 'DifferentialMail',
     'DifferentialRevision' => 'DifferentialDAO',
     'DifferentialRevisionCommentListView' => 'AphrontView',
     'DifferentialRevisionCommentView' => 'AphrontView',
     'DifferentialRevisionDetailView' => 'AphrontView',
     'DifferentialRevisionEditController' => 'DifferentialController',
     'DifferentialRevisionListController' => 'DifferentialController',
     'DifferentialRevisionUpdateHistoryView' => 'AphrontView',
     'DifferentialRevisionViewController' => 'DifferentialController',
     'DifferentialSubscribeController' => 'DifferentialController',
     'DifferentialViewTime' => 'DifferentialDAO',
     'DiffusionBranchTableView' => 'DiffusionView',
     'DiffusionBrowseController' => 'DiffusionController',
     'DiffusionBrowseFileController' => 'DiffusionController',
     'DiffusionBrowseTableView' => 'DiffusionView',
     'DiffusionChangeController' => 'DiffusionController',
     'DiffusionCommitChangeTableView' => 'DiffusionView',
     'DiffusionCommitController' => 'DiffusionController',
     'DiffusionController' => 'PhabricatorController',
     'DiffusionDiffController' => 'DiffusionController',
     'DiffusionGitBranchQuery' => 'DiffusionBranchQuery',
     'DiffusionGitBrowseQuery' => 'DiffusionBrowseQuery',
     'DiffusionGitDiffQuery' => 'DiffusionDiffQuery',
     'DiffusionGitFileContentQuery' => 'DiffusionFileContentQuery',
     'DiffusionGitHistoryQuery' => 'DiffusionHistoryQuery',
     'DiffusionGitLastModifiedQuery' => 'DiffusionLastModifiedQuery',
     'DiffusionGitRequest' => 'DiffusionRequest',
     'DiffusionHistoryController' => 'DiffusionController',
     'DiffusionHistoryTableView' => 'DiffusionView',
     'DiffusionHomeController' => 'DiffusionController',
     'DiffusionLastModifiedController' => 'DiffusionController',
     'DiffusionPathCompleteController' => 'DiffusionController',
     'DiffusionPathValidateController' => 'DiffusionController',
     'DiffusionRepositoryController' => 'DiffusionController',
     'DiffusionSvnBrowseQuery' => 'DiffusionBrowseQuery',
     'DiffusionSvnDiffQuery' => 'DiffusionDiffQuery',
     'DiffusionSvnFileContentQuery' => 'DiffusionFileContentQuery',
     'DiffusionSvnHistoryQuery' => 'DiffusionHistoryQuery',
     'DiffusionSvnLastModifiedQuery' => 'DiffusionLastModifiedQuery',
     'DiffusionSvnRequest' => 'DiffusionRequest',
     'DiffusionView' => 'AphrontView',
     'HeraldAction' => 'HeraldDAO',
     'HeraldApplyTranscript' => 'HeraldDAO',
     'HeraldCommitAdapter' => 'HeraldObjectAdapter',
     'HeraldCondition' => 'HeraldDAO',
     'HeraldController' => 'PhabricatorController',
     'HeraldDAO' => 'PhabricatorLiskDAO',
     'HeraldDeleteController' => 'HeraldController',
     'HeraldDifferentialRevisionAdapter' => 'HeraldObjectAdapter',
     'HeraldDryRunAdapter' => 'HeraldObjectAdapter',
     'HeraldHomeController' => 'HeraldController',
     'HeraldNewController' => 'HeraldController',
     'HeraldRule' => 'HeraldDAO',
     'HeraldRuleController' => 'HeraldController',
     'HeraldTestConsoleController' => 'HeraldController',
     'HeraldTranscript' => 'HeraldDAO',
     'HeraldTranscriptController' => 'HeraldController',
     'HeraldTranscriptListController' => 'HeraldController',
     'LiskIsolationTestCase' => 'PhabricatorTestCase',
     'LiskIsolationTestDAO' => 'LiskDAO',
     'ManiphestController' => 'PhabricatorController',
     'ManiphestDAO' => 'PhabricatorLiskDAO',
     'ManiphestTask' => 'ManiphestDAO',
     'ManiphestTaskDetailController' => 'ManiphestController',
     'ManiphestTaskEditController' => 'ManiphestController',
     'ManiphestTaskListController' => 'ManiphestController',
     'ManiphestTaskListView' => 'AphrontView',
     'ManiphestTaskSelectorSearchController' => 'ManiphestController',
     'ManiphestTaskSummaryView' => 'AphrontView',
     'ManiphestTransaction' => 'ManiphestDAO',
     'ManiphestTransactionDetailView' => 'AphrontView',
     'ManiphestTransactionListView' => 'AphrontView',
     'ManiphestTransactionSaveController' => 'ManiphestController',
     'Phabricator404Controller' => 'PhabricatorController',
     'PhabricatorAuthController' => 'PhabricatorController',
     'PhabricatorConduitAPIController' => 'PhabricatorConduitController',
     'PhabricatorConduitConnectionLog' => 'PhabricatorConduitDAO',
     'PhabricatorConduitConsoleController' => 'PhabricatorConduitController',
     'PhabricatorConduitController' => 'PhabricatorController',
     'PhabricatorConduitDAO' => 'PhabricatorLiskDAO',
     'PhabricatorConduitLogController' => 'PhabricatorConduitController',
     'PhabricatorConduitMethodCallLog' => 'PhabricatorConduitDAO',
     'PhabricatorController' => 'AphrontController',
     'PhabricatorDaemon' => 'PhutilDaemon',
     'PhabricatorDaemonConsoleController' => 'PhabricatorDaemonController',
     'PhabricatorDaemonController' => 'PhabricatorController',
     'PhabricatorDaemonDAO' => 'PhabricatorLiskDAO',
     'PhabricatorDaemonLog' => 'PhabricatorDaemonDAO',
     'PhabricatorDaemonLogEvent' => 'PhabricatorDaemonDAO',
     'PhabricatorDaemonLogViewController' => 'PhabricatorDaemonController',
     'PhabricatorDaemonTimelineConsoleController' => 'PhabricatorDaemonController',
     'PhabricatorDaemonTimelineEventController' => 'PhabricatorDaemonController',
     'PhabricatorDirectoryCategory' => 'PhabricatorDirectoryDAO',
     'PhabricatorDirectoryCategoryDeleteController' => 'PhabricatorDirectoryController',
     'PhabricatorDirectoryCategoryEditController' => 'PhabricatorDirectoryController',
     'PhabricatorDirectoryCategoryListController' => 'PhabricatorDirectoryController',
     'PhabricatorDirectoryController' => 'PhabricatorController',
     'PhabricatorDirectoryDAO' => 'PhabricatorLiskDAO',
     'PhabricatorDirectoryItem' => 'PhabricatorDirectoryDAO',
     'PhabricatorDirectoryItemDeleteController' => 'PhabricatorDirectoryController',
     'PhabricatorDirectoryItemEditController' => 'PhabricatorDirectoryController',
     'PhabricatorDirectoryItemListController' => 'PhabricatorDirectoryController',
     'PhabricatorDirectoryMainController' => 'PhabricatorDirectoryController',
     'PhabricatorDraft' => 'PhabricatorDraftDAO',
     'PhabricatorDraftDAO' => 'PhabricatorLiskDAO',
     'PhabricatorEditPreferencesController' => 'PhabricatorPreferencesController',
     'PhabricatorEmailLoginController' => 'PhabricatorAuthController',
     'PhabricatorEmailTokenController' => 'PhabricatorAuthController',
     'PhabricatorFile' => 'PhabricatorFileDAO',
     'PhabricatorFileController' => 'PhabricatorController',
     'PhabricatorFileDAO' => 'PhabricatorLiskDAO',
     'PhabricatorFileImageMacro' => 'PhabricatorFileDAO',
     'PhabricatorFileListController' => 'PhabricatorFileController',
     'PhabricatorFileStorageBlob' => 'PhabricatorFileDAO',
     'PhabricatorFileUploadController' => 'PhabricatorFileController',
     'PhabricatorFileViewController' => 'PhabricatorFileController',
     'PhabricatorGoodForNothingWorker' => 'PhabricatorWorker',
     'PhabricatorLiskDAO' => 'LiskDAO',
     'PhabricatorLoginController' => 'PhabricatorAuthController',
     'PhabricatorLogoutController' => 'PhabricatorAuthController',
     'PhabricatorMailImplementationAmazonSESAdapter' => 'PhabricatorMailImplementationPHPMailerLiteAdapter',
     'PhabricatorMailImplementationPHPMailerLiteAdapter' => 'PhabricatorMailImplementationAdapter',
+    'PhabricatorMailImplementationTestAdapter' => 'PhabricatorMailImplementationAdapter',
     'PhabricatorMetaMTAController' => 'PhabricatorController',
     'PhabricatorMetaMTADAO' => 'PhabricatorLiskDAO',
     'PhabricatorMetaMTADaemon' => 'PhabricatorDaemon',
     'PhabricatorMetaMTAListController' => 'PhabricatorMetaMTAController',
     'PhabricatorMetaMTAMail' => 'PhabricatorMetaMTADAO',
+    'PhabricatorMetaMTAMailTestCase' => 'PhabricatorTestCase',
     'PhabricatorMetaMTAMailingList' => 'PhabricatorMetaMTADAO',
     'PhabricatorMetaMTAMailingListEditController' => 'PhabricatorMetaMTAController',
     'PhabricatorMetaMTAMailingListsController' => 'PhabricatorMetaMTAController',
     'PhabricatorMetaMTASendController' => 'PhabricatorMetaMTAController',
     'PhabricatorMetaMTAViewController' => 'PhabricatorMetaMTAController',
     'PhabricatorOAuthDefaultRegistrationController' => 'PhabricatorOAuthRegistrationController',
     'PhabricatorOAuthDiagnosticsController' => 'PhabricatorAuthController',
     'PhabricatorOAuthFailureView' => 'AphrontView',
     'PhabricatorOAuthLoginController' => 'PhabricatorAuthController',
     'PhabricatorOAuthProviderFacebook' => 'PhabricatorOAuthProvider',
     'PhabricatorOAuthProviderGithub' => 'PhabricatorOAuthProvider',
     'PhabricatorOAuthRegistrationController' => 'PhabricatorAuthController',
     'PhabricatorOAuthUnlinkController' => 'PhabricatorAuthController',
     'PhabricatorOwnersController' => 'PhabricatorController',
     'PhabricatorOwnersDAO' => 'PhabricatorLiskDAO',
     'PhabricatorOwnersDeleteController' => 'PhabricatorOwnersController',
     'PhabricatorOwnersDetailController' => 'PhabricatorOwnersController',
     'PhabricatorOwnersEditController' => 'PhabricatorOwnersController',
     'PhabricatorOwnersListController' => 'PhabricatorOwnersController',
     'PhabricatorOwnersOwner' => 'PhabricatorOwnersDAO',
     'PhabricatorOwnersPackage' => 'PhabricatorOwnersDAO',
     'PhabricatorOwnersPath' => 'PhabricatorOwnersDAO',
     'PhabricatorPHID' => 'PhabricatorPHIDDAO',
     'PhabricatorPHIDAllocateController' => 'PhabricatorPHIDController',
     'PhabricatorPHIDController' => 'PhabricatorController',
     'PhabricatorPHIDDAO' => 'PhabricatorLiskDAO',
     'PhabricatorPHIDListController' => 'PhabricatorPHIDController',
     'PhabricatorPHIDLookupController' => 'PhabricatorPHIDController',
     'PhabricatorPeopleController' => 'PhabricatorController',
     'PhabricatorPeopleEditController' => 'PhabricatorPeopleController',
     'PhabricatorPeopleListController' => 'PhabricatorPeopleController',
     'PhabricatorPeopleProfileController' => 'PhabricatorPeopleController',
     'PhabricatorPeopleProfileEditController' => 'PhabricatorPeopleController',
     'PhabricatorPreferencesController' => 'PhabricatorController',
     'PhabricatorProject' => 'PhabricatorProjectDAO',
     'PhabricatorProjectAffiliation' => 'PhabricatorProjectDAO',
     'PhabricatorProjectAffiliationEditController' => 'PhabricatorProjectController',
     'PhabricatorProjectController' => 'PhabricatorController',
     'PhabricatorProjectDAO' => 'PhabricatorLiskDAO',
     'PhabricatorProjectEditController' => 'PhabricatorProjectController',
     'PhabricatorProjectListController' => 'PhabricatorProjectController',
     'PhabricatorProjectProfile' => 'PhabricatorProjectDAO',
     'PhabricatorProjectProfileController' => 'PhabricatorProjectController',
     'PhabricatorRedirectController' => 'PhabricatorController',
     'PhabricatorRemarkupRuleDifferential' => 'PhutilRemarkupRule',
     'PhabricatorRemarkupRuleDiffusion' => 'PhutilRemarkupRule',
     'PhabricatorRemarkupRuleImageMacro' => 'PhutilRemarkupRule',
     'PhabricatorRemarkupRuleManiphest' => 'PhutilRemarkupRule',
     'PhabricatorRepository' => 'PhabricatorRepositoryDAO',
     'PhabricatorRepositoryArcanistProject' => 'PhabricatorRepositoryDAO',
     'PhabricatorRepositoryArcanistProjectEditController' => 'PhabricatorRepositoryController',
     'PhabricatorRepositoryCommit' => 'PhabricatorRepositoryDAO',
     'PhabricatorRepositoryCommitChangeParserWorker' => 'PhabricatorRepositoryCommitParserWorker',
     'PhabricatorRepositoryCommitData' => 'PhabricatorRepositoryDAO',
     'PhabricatorRepositoryCommitDiscoveryDaemon' => 'PhabricatorRepositoryDaemon',
     'PhabricatorRepositoryCommitHeraldWorker' => 'PhabricatorRepositoryCommitParserWorker',
     'PhabricatorRepositoryCommitMessageParserWorker' => 'PhabricatorRepositoryCommitParserWorker',
     'PhabricatorRepositoryCommitParserWorker' => 'PhabricatorWorker',
     'PhabricatorRepositoryCommitTaskDaemon' => 'PhabricatorRepositoryDaemon',
     'PhabricatorRepositoryController' => 'PhabricatorController',
     'PhabricatorRepositoryCreateController' => 'PhabricatorRepositoryController',
     'PhabricatorRepositoryDAO' => 'PhabricatorLiskDAO',
     'PhabricatorRepositoryDaemon' => 'PhabricatorDaemon',
     'PhabricatorRepositoryDefaultCommitMessageDetailParser' => 'PhabricatorRepositoryCommitMessageDetailParser',
     'PhabricatorRepositoryEditController' => 'PhabricatorRepositoryController',
     'PhabricatorRepositoryGitCommitChangeParserWorker' => 'PhabricatorRepositoryCommitChangeParserWorker',
     'PhabricatorRepositoryGitCommitDiscoveryDaemon' => 'PhabricatorRepositoryCommitDiscoveryDaemon',
     'PhabricatorRepositoryGitCommitMessageParserWorker' => 'PhabricatorRepositoryCommitMessageParserWorker',
     'PhabricatorRepositoryGitFetchDaemon' => 'PhabricatorRepositoryDaemon',
     'PhabricatorRepositoryGitHubNotification' => 'PhabricatorRepositoryDAO',
     'PhabricatorRepositoryGitHubPostReceiveController' => 'PhabricatorRepositoryController',
     'PhabricatorRepositoryListController' => 'PhabricatorRepositoryController',
     'PhabricatorRepositoryShortcut' => 'PhabricatorRepositoryDAO',
     'PhabricatorRepositorySvnCommitChangeParserWorker' => 'PhabricatorRepositoryCommitChangeParserWorker',
     'PhabricatorRepositorySvnCommitDiscoveryDaemon' => 'PhabricatorRepositoryCommitDiscoveryDaemon',
     'PhabricatorRepositorySvnCommitMessageParserWorker' => 'PhabricatorRepositoryCommitMessageParserWorker',
     'PhabricatorSearchBaseController' => 'PhabricatorController',
     'PhabricatorSearchController' => 'PhabricatorSearchBaseController',
     'PhabricatorSearchDAO' => 'PhabricatorLiskDAO',
     'PhabricatorSearchDifferentialIndexer' => 'PhabricatorSearchDocumentIndexer',
     'PhabricatorSearchDocument' => 'PhabricatorSearchDAO',
     'PhabricatorSearchDocumentField' => 'PhabricatorSearchDAO',
     'PhabricatorSearchDocumentRelationship' => 'PhabricatorSearchDAO',
     'PhabricatorSearchManiphestIndexer' => 'PhabricatorSearchDocumentIndexer',
     'PhabricatorSearchMySQLExecutor' => 'PhabricatorSearchExecutor',
     'PhabricatorSearchQuery' => 'PhabricatorSearchDAO',
     'PhabricatorStandardPageView' => 'AphrontPageView',
     'PhabricatorStatusController' => 'PhabricatorController',
     'PhabricatorTaskmasterDaemon' => 'PhabricatorDaemon',
     'PhabricatorTestCase' => 'ArcanistPhutilTestCase',
     'PhabricatorTimelineCursor' => 'PhabricatorTimelineDAO',
     'PhabricatorTimelineDAO' => 'PhabricatorLiskDAO',
     'PhabricatorTimelineEvent' => 'PhabricatorTimelineDAO',
     'PhabricatorTimelineEventData' => 'PhabricatorTimelineDAO',
     'PhabricatorTypeaheadCommonDatasourceController' => 'PhabricatorTypeaheadDatasourceController',
     'PhabricatorTypeaheadDatasourceController' => 'PhabricatorController',
     'PhabricatorUIExampleController' => 'PhabricatorController',
     'PhabricatorUIExampleRenderController' => 'PhabricatorUIExampleController',
     'PhabricatorUIListFilterExample' => 'PhabricatorUIExample',
     'PhabricatorUIPagerExample' => 'PhabricatorUIExample',
     'PhabricatorUser' => 'PhabricatorUserDAO',
     'PhabricatorUserDAO' => 'PhabricatorLiskDAO',
     'PhabricatorUserOAuthInfo' => 'PhabricatorUserDAO',
     'PhabricatorUserPreferences' => 'PhabricatorUserDAO',
     'PhabricatorUserProfile' => 'PhabricatorUserDAO',
     'PhabricatorUserSettingsController' => 'PhabricatorPeopleController',
     'PhabricatorWorkerDAO' => 'PhabricatorLiskDAO',
     'PhabricatorWorkerTask' => 'PhabricatorWorkerDAO',
     'PhabricatorWorkerTaskData' => 'PhabricatorWorkerDAO',
     'PhabricatorWorkerTaskDetailController' => 'PhabricatorDaemonController',
     'PhabricatorXHPASTViewController' => 'PhabricatorController',
     'PhabricatorXHPASTViewDAO' => 'PhabricatorLiskDAO',
     'PhabricatorXHPASTViewFrameController' => 'PhabricatorXHPASTViewController',
     'PhabricatorXHPASTViewFramesetController' => 'PhabricatorXHPASTViewController',
     'PhabricatorXHPASTViewInputController' => 'PhabricatorXHPASTViewPanelController',
     'PhabricatorXHPASTViewPanelController' => 'PhabricatorXHPASTViewController',
     'PhabricatorXHPASTViewParseTree' => 'PhabricatorXHPASTViewDAO',
     'PhabricatorXHPASTViewRunController' => 'PhabricatorXHPASTViewController',
     'PhabricatorXHPASTViewStreamController' => 'PhabricatorXHPASTViewPanelController',
     'PhabricatorXHPASTViewTreeController' => 'PhabricatorXHPASTViewPanelController',
     'PhabricatorXHProfController' => 'PhabricatorController',
     'PhabricatorXHProfProfileController' => 'PhabricatorXHProfController',
     'PhabricatorXHProfProfileSymbolView' => 'AphrontView',
     'PhabricatorXHProfProfileTopLevelView' => 'AphrontView',
   ),
   'requires_interface' =>
   array(
   ),
 ));
diff --git a/src/applications/differential/mail/base/DifferentialMail.php b/src/applications/differential/mail/base/DifferentialMail.php
index a8a680867f..4f1e98d073 100644
--- a/src/applications/differential/mail/base/DifferentialMail.php
+++ b/src/applications/differential/mail/base/DifferentialMail.php
@@ -1,320 +1,284 @@
 <?php
 
 /*
  * Copyright 2011 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *   http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 abstract class DifferentialMail {
 
   const SUBJECT_PREFIX  = '[Differential]';
 
   protected $to = array();
   protected $cc = array();
 
   protected $actorHandle;
 
   protected $revision;
   protected $comment;
   protected $changesets;
   protected $inlineComments;
   protected $isFirstMailAboutRevision;
   protected $isFirstMailToRecipients;
   protected $heraldTranscriptURI;
   protected $heraldRulesHeader;
 
   abstract protected function renderSubject();
   abstract protected function renderBody();
 
   public function setActorHandle($actor_handle) {
     $this->actorHandle = $actor_handle;
     return $this;
   }
 
   public function getActorHandle() {
     return $this->actorHandle;
   }
 
   protected function getActorName() {
     $handle = $this->getActorHandle();
     if ($handle) {
       return $handle->getName();
     }
     return '???';
   }
 
   public function setXHeraldRulesHeader($header) {
     $this->heraldRulesHeader = $header;
     return $this;
   }
 
   public function send() {
     $to_phids = $this->getToPHIDs();
     if (!$to_phids) {
       throw new Exception('No "To:" users provided!');
     }
 
-    $message_id = $this->getMessageID();
-
     $cc_phids = $this->getCCPHIDs();
     $subject  = $this->buildSubject();
     $body     = $this->buildBody();
 
     $mail = new PhabricatorMetaMTAMail();
     $handle = $this->getActorHandle();
     $reply = $this->getReplyHandlerEmailAddress();
     if ($handle) {
       $mail->setFrom($handle->getPHID());
       if ($reply) {
         $mail->setReplyTo($this->getReplyHandlerEmailAddress());
       }
     } else {
       if ($reply) {
         $mail->setFrom($this->getReplyHandlerEmailAddress());
       }
     }
 
     $mail
       ->addTos($to_phids)
       ->addCCs($cc_phids)
       ->setSubject($subject)
       ->setBody($body)
       ->setIsHTML($this->shouldMarkMailAsHTML())
-      ->addHeader('Thread-Topic', $this->getRevision()->getTitle())
-      ->addHeader('Thread-Index', $this->generateThreadIndex());
+      ->addHeader('Thread-Topic', $this->getRevision()->getTitle());
 
-    if ($this->isFirstMailAboutRevision()) {
-      $mail->addHeader('Message-ID',  $message_id);
-    } else {
-      $mail->addHeader('In-Reply-To', $message_id);
-      $mail->addHeader('References',  $message_id);
-    }
+    $mail->setThreadID(
+      $this->getThreadID(),
+      $this->isFirstMailAboutRevision());
 
     if ($this->heraldRulesHeader) {
       $mail->addHeader('X-Herald-Rules', $this->heraldRulesHeader);
     }
 
     $mail->setRelatedPHID($this->getRevision()->getPHID());
 
     // Save this to the MetaMTA queue for later delivery to the MTA.
     $mail->save();
   }
 
   protected function buildSubject() {
     return self::SUBJECT_PREFIX.' '.$this->renderSubject();
   }
 
   protected function shouldMarkMailAsHTML() {
     return false;
   }
 
   protected function buildBody() {
 
     $actions = array();
     $body = $this->renderBody();
 /*
     $body .= <<<EOTEXT
 
 ACTIONS
   Reply to comment, or !accept, !reject, !abandon, !resign, or !showdiff.
 
 EOTEXT;
 */
 
     if ($this->getHeraldTranscriptURI() && $this->isFirstMailToRecipients()) {
       $manage_uri = PhabricatorEnv::getProductionURI(
         '/herald/view/differential/');
 
       $xscript_uri = $this->getHeraldTranscriptURI();
       $body .= <<<EOTEXT
 
 MANAGE HERALD DIFFERENTIAL RULES
   {$manage_uri}
 
 WHY DID I GET THIS EMAIL?
   {$xscript_uri}
 
 Tip: use the X-Herald-Rules header to filter Herald messages in your client.
 
 EOTEXT;
     }
 
     return $body;
   }
 
   protected function getReplyHandlerEmailAddress() {
     return null;
     // TODO
     $phid = $this->getRevision()->getPHID();
     $server = 'todo.example.com';
     return "differential+{$phid}@{$server}";
   }
 
   protected function formatText($text) {
     $text = explode("\n", $text);
     foreach ($text as &$line) {
       $line = rtrim('  '.$line);
     }
     unset($line);
     return implode("\n", $text);
   }
 
   public function setToPHIDs(array $to) {
     $this->to = $this->filterContactPHIDs($to);
     return $this;
   }
 
   public function setCCPHIDs(array $cc) {
     $this->cc = $this->filterContactPHIDs($cc);
     return $this;
   }
 
   protected function filterContactPHIDs(array $phids) {
     return $phids;
 
     // TODO: actually do this?
 
     // Differential revisions use Subscriptions for CCs, so any arbitrary
     // PHID can end up CC'd to them. Only try to actually send email PHIDs
     // which have ToolsHandle types that are marked emailable. If we don't
     // filter here, sending the email will fail.
 /*
     $handles = array();
     prep(new ToolsHandleData($phids, $handles));
     foreach ($handles as $phid => $handle) {
       if (!$handle->isEmailable()) {
         unset($handles[$phid]);
       }
     }
     return array_keys($handles);
 */
   }
 
   protected function getToPHIDs() {
     return $this->to;
   }
 
   protected function getCCPHIDs() {
     return $this->cc;
   }
 
   public function setRevision($revision) {
     $this->revision = $revision;
     return $this;
   }
 
   public function getRevision() {
     return $this->revision;
   }
 
-  protected function getMessageID() {
+  protected function getThreadID() {
     $phid = $this->getRevision()->getPHID();
     $domain = PhabricatorEnv::getEnvConfig('metamta.domain');
     return "<differential-rev-{$phid}-req@{$domain}>";
   }
 
   public function setComment($comment) {
     $this->comment = $comment;
     return $this;
   }
 
   public function getComment() {
     return $this->comment;
   }
 
   public function setChangesets($changesets) {
     $this->changesets = $changesets;
     return $this;
   }
 
   public function getChangesets() {
     return $this->changesets;
   }
 
   public function setInlineComments(array $inline_comments) {
     $this->inlineComments = $inline_comments;
     return $this;
   }
 
   public function getInlineComments() {
     return $this->inlineComments;
   }
 
   public function renderRevisionDetailLink() {
     $uri = $this->getRevisionURI();
     return "REVISION DETAIL\n  {$uri}";
   }
 
   public function getRevisionURI() {
     return PhabricatorEnv::getProductionURI('/D'.$this->getRevision()->getID());
   }
 
   public function setIsFirstMailToRecipients($first) {
     $this->isFirstMailToRecipients = $first;
     return $this;
   }
 
   public function isFirstMailToRecipients() {
     return $this->isFirstMailToRecipients;
   }
 
   public function setIsFirstMailAboutRevision($first) {
     $this->isFirstMailAboutRevision = $first;
     return $this;
   }
 
   public function isFirstMailAboutRevision() {
     return $this->isFirstMailAboutRevision;
   }
 
-  protected function generateThreadIndex() {
-    // When threading, Outlook ignores the 'References' and 'In-Reply-To'
-    // headers that most clients use. Instead, it uses a custom 'Thread-Index'
-    // header. The format of this header is something like this (from
-    // camel-exchange-folder.c in Evolution Exchange):
-
-    /* A new post to a folder gets a 27-byte-long thread index. (The value
-     * is apparently unique but meaningless.) Each reply to a post gets a
-     * 32-byte-long thread index whose first 27 bytes are the same as the
-     * parent's thread index. Each reply to any of those gets a
-     * 37-byte-long thread index, etc. The Thread-Index header contains a
-     * base64 representation of this value.
-     */
-
-    // The specific implementation uses a 27-byte header for the first email
-    // a recipient receives, and a random 5-byte suffix (32 bytes total)
-    // thereafter. This means that all the replies are (incorrectly) siblings,
-    // but it would be very difficult to keep track of the entire tree and this
-    // gets us reasonable client behavior.
-
-    $base = substr(md5($this->getRevision()->getPHID()), 0, 27);
-    if (!$this->isFirstMailAboutRevision()) {
-      // not totally sure, but it seems like outlook orders replies by
-      // thread-index rather than timestamp, so to get these to show up in the
-      // right order we use the time as the last 4 bytes.
-      $base .= ' ' . pack("N", time());
-    }
-    return base64_encode($base);
-  }
-
   public function setHeraldTranscriptURI($herald_transcript_uri) {
     $this->heraldTranscriptURI = $herald_transcript_uri;
     return $this;
   }
 
   public function getHeraldTranscriptURI() {
     return $this->heraldTranscriptURI;
   }
 
 }
diff --git a/src/applications/metamta/adapter/amazonses/PhabricatorMailImplementationAmazonSESAdapter.php b/src/applications/metamta/adapter/amazonses/PhabricatorMailImplementationAmazonSESAdapter.php
index d7b964175b..6ee8395566 100644
--- a/src/applications/metamta/adapter/amazonses/PhabricatorMailImplementationAmazonSESAdapter.php
+++ b/src/applications/metamta/adapter/amazonses/PhabricatorMailImplementationAmazonSESAdapter.php
@@ -1,43 +1,48 @@
 <?php
 
 /*
  * Copyright 2011 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *   http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 class PhabricatorMailImplementationAmazonSESAdapter
   extends PhabricatorMailImplementationPHPMailerLiteAdapter {
 
   private $message;
   private $isHTML;
 
   public function __construct() {
     parent::__construct();
     $this->mailer->Mailer = 'amazon-ses';
     $this->mailer->customMailer = $this;
   }
 
+  public function supportsMessageIDHeader() {
+    // Amazon SES will ignore any Message-ID we provide.
+    return false;
+  }
+
   public function executeSend($body) {
     $key = PhabricatorEnv::getEnvConfig('amazon-ses.access-key');
     $secret = PhabricatorEnv::getEnvConfig('amazon-ses.secret-key');
 
     $root = phutil_get_library_root('phabricator');
     $root = dirname($root);
     require_once $root.'/externals/amazon-ses/ses.php';
 
-    $service = new SimpleEmailService($key, $secret);
+    $service = newv('SimpleEmailService', array($key, $secret));
     return $service->sendRawEmail($body);
   }
 
 }
diff --git a/src/applications/metamta/adapter/amazonses/__init__.php b/src/applications/metamta/adapter/amazonses/__init__.php
index 5e546c1a3e..884af1e1c5 100644
--- a/src/applications/metamta/adapter/amazonses/__init__.php
+++ b/src/applications/metamta/adapter/amazonses/__init__.php
@@ -1,15 +1,16 @@
 <?php
 /**
  * This file is automatically generated. Lint this module to rebuild it.
  * @generated
  */
 
 
 
 phutil_require_module('phabricator', 'applications/metamta/adapter/phpmailerlite');
 phutil_require_module('phabricator', 'infrastructure/env');
 
 phutil_require_module('phutil', 'moduleutils');
+phutil_require_module('phutil', 'utils');
 
 
 phutil_require_source('PhabricatorMailImplementationAmazonSESAdapter.php');
diff --git a/src/applications/metamta/adapter/base/PhabricatorMailImplementationAdapter.php b/src/applications/metamta/adapter/base/PhabricatorMailImplementationAdapter.php
index 10746a01c3..e00e99b2e2 100644
--- a/src/applications/metamta/adapter/base/PhabricatorMailImplementationAdapter.php
+++ b/src/applications/metamta/adapter/base/PhabricatorMailImplementationAdapter.php
@@ -1,32 +1,38 @@
 <?php
 
 /*
  * Copyright 2011 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *   http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 abstract class PhabricatorMailImplementationAdapter {
 
   abstract public function setFrom($email);
   abstract public function addReplyTo($email);
   abstract public function addTos(array $emails);
   abstract public function addCCs(array $emails);
   abstract public function addHeader($header_name, $header_value);
   abstract public function setBody($body);
   abstract public function setSubject($subject);
   abstract public function setIsHTML($is_html);
 
+  /**
+   * Some mailers, notably Amazon SES, do not support us setting a specific
+   * Message-ID header.
+   */
+  abstract public function supportsMessageIDHeader();
+
   abstract public function send();
 
 }
diff --git a/src/applications/metamta/adapter/phpmailerlite/PhabricatorMailImplementationPHPMailerLiteAdapter.php b/src/applications/metamta/adapter/phpmailerlite/PhabricatorMailImplementationPHPMailerLiteAdapter.php
index af131b28fb..c17ca045e2 100644
--- a/src/applications/metamta/adapter/phpmailerlite/PhabricatorMailImplementationPHPMailerLiteAdapter.php
+++ b/src/applications/metamta/adapter/phpmailerlite/PhabricatorMailImplementationPHPMailerLiteAdapter.php
@@ -1,85 +1,89 @@
 <?php
 
 /*
  * Copyright 2011 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *   http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 class PhabricatorMailImplementationPHPMailerLiteAdapter
   extends PhabricatorMailImplementationAdapter {
 
   public function __construct() {
     $root = phutil_get_library_root('phabricator');
     $root = dirname($root);
     require_once $root.'/externals/phpmailer/class.phpmailer-lite.php';
     $this->mailer = newv('PHPMailerLite', array($use_exceptions = true));
   }
 
+  public function supportsMessageIDHeader() {
+    return true;
+  }
+
   public function setFrom($email) {
     $this->mailer->SetFrom($email, '', $crazy_side_effects = false);
     return $this;
   }
 
   public function addReplyTo($email) {
     $this->mailer->AddReplyTo($email);
     return $this;
   }
 
   public function addTos(array $emails) {
     foreach ($emails as $email) {
       $this->mailer->AddAddress($email);
     }
     return $this;
   }
 
   public function addCCs(array $emails) {
     foreach ($emails as $email) {
       $this->mailer->AddCC($email);
     }
     return $this;
   }
 
   public function addHeader($header_name, $header_value) {
     if (strtolower($header_name) == 'message-id') {
       $this->mailer->MessageID = $header_value;
     } else {
       $this->mailer->AddCustomHeader($header_name.': '.$header_value);
     }
     return $this;
   }
 
   public function setBody($body) {
     $this->mailer->Body = $body;
     return $this;
   }
 
   public function setSubject($subject) {
     $this->mailer->Subject = $subject;
     return $this;
   }
 
   public function setIsHTML($is_html) {
     $this->mailer->IsHTML(true);
     return $this;
   }
 
   public function hasValidRecipients() {
     return true;
   }
 
   public function send() {
     return $this->mailer->Send();
   }
 
 }
diff --git a/src/applications/metamta/adapter/phpmailerlite/PhabricatorMailImplementationPHPMailerLiteAdapter.php b/src/applications/metamta/adapter/test/PhabricatorMailImplementationTestAdapter.php
similarity index 60%
copy from src/applications/metamta/adapter/phpmailerlite/PhabricatorMailImplementationPHPMailerLiteAdapter.php
copy to src/applications/metamta/adapter/test/PhabricatorMailImplementationTestAdapter.php
index af131b28fb..86c3518570 100644
--- a/src/applications/metamta/adapter/phpmailerlite/PhabricatorMailImplementationPHPMailerLiteAdapter.php
+++ b/src/applications/metamta/adapter/test/PhabricatorMailImplementationTestAdapter.php
@@ -1,85 +1,90 @@
 <?php
 
 /*
  * Copyright 2011 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *   http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
-class PhabricatorMailImplementationPHPMailerLiteAdapter
+/**
+ * Mail adapter that doesn't actually send any email, for writing unit tests
+ * against.
+ */
+class PhabricatorMailImplementationTestAdapter
   extends PhabricatorMailImplementationAdapter {
 
-  public function __construct() {
-    $root = phutil_get_library_root('phabricator');
-    $root = dirname($root);
-    require_once $root.'/externals/phpmailer/class.phpmailer-lite.php';
-    $this->mailer = newv('PHPMailerLite', array($use_exceptions = true));
+  private $guts = array();
+  private $config;
+
+  public function __construct(array $config) {
+    $this->config = $config;
   }
 
   public function setFrom($email) {
-    $this->mailer->SetFrom($email, '', $crazy_side_effects = false);
+    $this->guts['from'] = $email;
     return $this;
   }
 
   public function addReplyTo($email) {
-    $this->mailer->AddReplyTo($email);
+    $this->guts['reply-to'] = $email;
     return $this;
   }
 
   public function addTos(array $emails) {
     foreach ($emails as $email) {
-      $this->mailer->AddAddress($email);
+      $this->guts['tos'][] = $email;
     }
     return $this;
   }
 
   public function addCCs(array $emails) {
     foreach ($emails as $email) {
-      $this->mailer->AddCC($email);
+      $this->guts['ccs'][] = $email;
     }
     return $this;
   }
 
   public function addHeader($header_name, $header_value) {
-    if (strtolower($header_name) == 'message-id') {
-      $this->mailer->MessageID = $header_value;
-    } else {
-      $this->mailer->AddCustomHeader($header_name.': '.$header_value);
-    }
+    $this->guts['headers'][] = array($header_name, $header_value);
     return $this;
   }
 
   public function setBody($body) {
-    $this->mailer->Body = $body;
+    $this->guts['body'] = $body;
     return $this;
   }
 
   public function setSubject($subject) {
-    $this->mailer->Subject = $subject;
+    $this->guts['subject'] = $subject;
     return $this;
   }
 
   public function setIsHTML($is_html) {
-    $this->mailer->IsHTML(true);
+    $this->guts['is-html'] = $is_html;
     return $this;
   }
 
-  public function hasValidRecipients() {
-    return true;
+  public function supportsMessageIDHeader() {
+    return $this->config['supportsMessageIDHeader'];
   }
 
   public function send() {
-    return $this->mailer->Send();
+    $this->guts['did-send'] = true;
+    return true;
+  }
+
+  public function getGuts() {
+    return $this->guts;
   }
 
 }
diff --git a/src/applications/metamta/adapter/test/__init__.php b/src/applications/metamta/adapter/test/__init__.php
new file mode 100644
index 0000000000..513e48bb6d
--- /dev/null
+++ b/src/applications/metamta/adapter/test/__init__.php
@@ -0,0 +1,12 @@
+<?php
+/**
+ * This file is automatically generated. Lint this module to rebuild it.
+ * @generated
+ */
+
+
+
+phutil_require_module('phabricator', 'applications/metamta/adapter/base');
+
+
+phutil_require_source('PhabricatorMailImplementationTestAdapter.php');
diff --git a/src/applications/metamta/storage/mail/PhabricatorMetaMTAMail.php b/src/applications/metamta/storage/mail/PhabricatorMetaMTAMail.php
index b58ae627ad..0507acfe92 100644
--- a/src/applications/metamta/storage/mail/PhabricatorMetaMTAMail.php
+++ b/src/applications/metamta/storage/mail/PhabricatorMetaMTAMail.php
@@ -1,280 +1,355 @@
 <?php
 
 /*
  * Copyright 2011 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *   http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 /**
  * See #394445 for an explanation of why this thing even exists.
  */
 class PhabricatorMetaMTAMail extends PhabricatorMetaMTADAO {
 
   const STATUS_QUEUE = 'queued';
   const STATUS_SENT  = 'sent';
   const STATUS_FAIL  = 'fail';
 
   const MAX_RETRIES   = 250;
   const RETRY_DELAY   = 5;
 
   protected $parameters;
   protected $status;
   protected $message;
   protected $retryCount;
   protected $nextRetry;
   protected $relatedPHID;
 
+  private $skipSendOnSave;
+
   public function __construct() {
 
     $this->status     = self::STATUS_QUEUE;
     $this->retryCount = 0;
     $this->nextRetry  = time();
     $this->parameters = array();
 
     parent::__construct();
   }
 
   public function getConfiguration() {
     return array(
       self::CONFIG_SERIALIZATION => array(
         'parameters'  => self::SERIALIZATION_JSON,
       ),
     ) + parent::getConfiguration();
   }
 
   protected function setParam($param, $value) {
     $this->parameters[$param] = $value;
     return $this;
   }
 
   protected function getParam($param) {
     return idx($this->parameters, $param);
   }
 
   public function getSubject() {
     return $this->getParam('subject');
   }
 
   public function addTos(array $phids) {
     $this->setParam('to', $phids);
     return $this;
   }
 
   public function addCCs(array $phids) {
     $this->setParam('cc', $phids);
     return $this;
   }
 
   public function addHeader($name, $value) {
     $this->parameters['headers'][$name] = $value;
     return $this;
   }
 
   public function setFrom($from) {
     $this->setParam('from', $from);
     return $this;
   }
 
   public function setReplyTo($reply_to) {
     $this->setParam('reply-to', $reply_to);
     return $this;
   }
 
   public function setSubject($subject) {
     $this->setParam('subject', $subject);
     return $this;
   }
 
   public function setBody($body) {
     $this->setParam('body', $body);
     return $this;
   }
 
   public function setIsHTML($html) {
     $this->setParam('is-html', $html);
     return $this;
   }
 
   public function getSimulatedFailureCount() {
     return nonempty($this->getParam('simulated-failures'), 0);
   }
 
   public function setSimulatedFailureCount($count) {
     $this->setParam('simulated-failures', $count);
     return $this;
   }
 
+  /**
+   * Use this method to set an ID used for message threading. MetaMTA will
+   * set appropriate headers (Message-ID, In-Reply-To, References and
+   * Thread-Index) based on the capabilities of the underlying mailer.
+   *
+   * @param string  Unique identifier, appropriate for use in a Message-ID,
+   *                In-Reply-To or References headers.
+   * @param bool    If true, indicates this is the first message in the thread.
+   * @return this
+   */
+  public function setThreadID($thread_id, $is_first_message = false) {
+    $this->setParam('thread-id', $thread_id);
+    $this->setParam('is-first-message', $is_first_message);
+    return $this;
+  }
+
   public function save() {
     $try_send = (PhabricatorEnv::getEnvConfig('metamta.send-immediately')) &&
-                (!$this->getID());
+                (!$this->getID()) &&
+                (!$this->skipSendOnSave);
 
     $ret = parent::save();
 
     if ($try_send) {
       $this->sendNow();
     }
 
     return $ret;
   }
 
-  private function buildDefaultMailer() {
+  public function buildDefaultMailer() {
     $class_name = PhabricatorEnv::getEnvConfig('metamta.mail-adapter');
     PhutilSymbolLoader::loadClass($class_name);
     return newv($class_name, array());
   }
 
+  /**
+   * Attempt to deliver an email immediately, in this process.
+   *
+   * @param bool  Try to deliver this email even if it has already been
+   *              delivered or is in backoff after a failed delivery attempt.
+   * @param PhabricatorMailImplementationAdapter Use a specific mail adapter,
+   *              instead of the default.
+   *
+   * @return void
+   */
   public function sendNow(
     $force_send = false,
     PhabricatorMailImplementationAdapter $mailer = null) {
 
     if ($mailer === null) {
       $mailer = $this->buildDefaultMailer();
     }
 
     if (!$force_send) {
       if ($this->getStatus() != self::STATUS_QUEUE) {
         throw new Exception("Trying to send an already-sent mail!");
       }
 
       if (time() < $this->getNextRetry()) {
         throw new Exception("Trying to send an email before next retry!");
       }
     }
 
+    $this->skipSendOnSave = true;
+
     try {
       $parameters = $this->parameters;
       $phids = array();
       foreach ($parameters as $key => $value) {
         switch ($key) {
           case 'from':
           case 'to':
           case 'cc':
             if (!is_array($value)) {
               $value = array($value);
             }
             foreach (array_filter($value) as $phid) {
               $phids[] = $phid;
             }
             break;
         }
       }
 
       $handles = id(new PhabricatorObjectHandleData($phids))
         ->loadHandles();
 
       $params = $this->parameters;
       $default = PhabricatorEnv::getEnvConfig('metamta.default-address');
       if (empty($params['from'])) {
         $mailer->setFrom($default);
       } else if (!PhabricatorEnv::getEnvConfig('metamta.can-send-as-user')) {
         $from = $params['from'];
         if (empty($params['reply-to'])) {
           $params['reply-to'] = $handles[$from]->getEmail();
         }
         $mailer->setFrom($default);
         unset($params['from']);
       }
 
+      $is_first = !empty($params['is-first-message']);
+      unset($params['is-first-message']);
+
       foreach ($params as $key => $value) {
         switch ($key) {
           case 'from':
             $mailer->setFrom($handles[$value]->getEmail());
             break;
           case 'reply-to':
             $mailer->addReplyTo($value);
             break;
           case 'to':
             $emails = array();
             foreach ($value as $phid) {
               $emails[] = $handles[$phid]->getEmail();
             }
             $mailer->addTos($emails);
             break;
           case 'cc':
             $emails = array();
             foreach ($value as $phid) {
               $emails[] = $handles[$phid]->getEmail();
             }
             $mailer->addCCs($emails);
             break;
           case 'headers':
             foreach ($value as $header_key => $header_value) {
               $mailer->addHeader($header_key, $header_value);
             }
             break;
           case 'body':
             $mailer->setBody($value);
             break;
           case 'subject':
             $mailer->setSubject($value);
             break;
           case 'is-html':
             if ($value) {
               $mailer->setIsHTML(true);
             }
             break;
+          case 'thread-id':
+            if ($is_first && $mailer->supportsMessageIDHeader()) {
+              $mailer->addHeader('Message-ID',  $value);
+            } else {
+              $mailer->addHeader('In-Reply-To', $value);
+              $mailer->addHeader('References',  $value);
+            }
+            $thread_index = $this->generateThreadIndex($value, $is_first);
+            $mailer->addHeader('Thread-Index', $thread_index);
+            break;
           default:
             // Just discard.
         }
       }
 
       $mailer->addHeader('X-Mail-Transport-Agent', 'MetaMTA');
 
     } catch (Exception $ex) {
       $this->setStatus(self::STATUS_FAIL);
       $this->setMessage($ex->getMessage());
       $this->save();
       return;
     }
 
     if ($this->getRetryCount() < $this->getSimulatedFailureCount()) {
       $ok = false;
       $error = 'Simulated failure.';
     } else {
       try {
         $ok = $mailer->send();
         $error = null;
       } catch (Exception $ex) {
         $ok = false;
         $error = $ex->getMessage()."\n".$ex->getTraceAsString();
       }
     }
 
     if (!$ok) {
       $this->setMessage($error);
       if ($this->getRetryCount() > self::MAX_RETRIES) {
         $this->setStatus(self::STATUS_FAIL);
       } else {
         $this->setRetryCount($this->getRetryCount() + 1);
         $next_retry = time() + ($this->getRetryCount() * self::RETRY_DELAY);
         $this->setNextRetry($next_retry);
       }
     } else {
       $this->setStatus(self::STATUS_SENT);
     }
 
     $this->save();
   }
 
   public static function getReadableStatus($status_code) {
     static $readable = array(
       self::STATUS_QUEUE => "Queued for Delivery",
       self::STATUS_FAIL  => "Delivery Failed",
       self::STATUS_SENT  => "Sent",
     );
     $status_code = coalesce($status_code, '?');
     return idx($readable, $status_code, $status_code);
   }
 
+  private function generateThreadIndex($seed, $is_first_mail) {
+    // When threading, Outlook ignores the 'References' and 'In-Reply-To'
+    // headers that most clients use. Instead, it uses a custom 'Thread-Index'
+    // header. The format of this header is something like this (from
+    // camel-exchange-folder.c in Evolution Exchange):
+
+    /* A new post to a folder gets a 27-byte-long thread index. (The value
+     * is apparently unique but meaningless.) Each reply to a post gets a
+     * 32-byte-long thread index whose first 27 bytes are the same as the
+     * parent's thread index. Each reply to any of those gets a
+     * 37-byte-long thread index, etc. The Thread-Index header contains a
+     * base64 representation of this value.
+     */
+
+    // The specific implementation uses a 27-byte header for the first email
+    // a recipient receives, and a random 5-byte suffix (32 bytes total)
+    // thereafter. This means that all the replies are (incorrectly) siblings,
+    // but it would be very difficult to keep track of the entire tree and this
+    // gets us reasonable client behavior.
+
+    $base = substr(md5($seed), 0, 27);
+    if (!$is_first_mail) {
+      // Not totally sure, but it seems like outlook orders replies by
+      // thread-index rather than timestamp, so to get these to show up in the
+      // right order we use the time as the last 4 bytes.
+      $base .= ' '.pack('N', time());
+    }
+
+    return base64_encode($base);
+  }
+
 }
diff --git a/src/applications/metamta/storage/mail/__tests__/PhabricatorMetaMTAMailTestCase.php b/src/applications/metamta/storage/mail/__tests__/PhabricatorMetaMTAMailTestCase.php
new file mode 100644
index 0000000000..54bcc2c625
--- /dev/null
+++ b/src/applications/metamta/storage/mail/__tests__/PhabricatorMetaMTAMailTestCase.php
@@ -0,0 +1,77 @@
+<?php
+
+/*
+ * Copyright 2011 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class PhabricatorMetaMTAMailTestCase extends PhabricatorTestCase {
+
+  public function testThreadIDHeaders() {
+    $this->runThreadIDHeadersWithConfiguration(true, true);
+    $this->runThreadIDHeadersWithConfiguration(true, false);
+    $this->runThreadIDHeadersWithConfiguration(false, true);
+    $this->runThreadIDHeadersWithConfiguration(false, false);
+  }
+
+  private function runThreadIDHeadersWithConfiguration(
+    $supports_message_id,
+    $is_first_mail) {
+
+    $mailer = new PhabricatorMailImplementationTestAdapter(
+      array(
+        'supportsMessageIDHeader' => $supports_message_id,
+      ));
+
+    $thread_id = '<somethread-12345@somedomain.tld>';
+
+    $mail = new PhabricatorMetaMTAMail();
+    $mail->setThreadID($thread_id, $is_first_mail);
+    $mail->sendNow($force = true, $mailer);
+
+    $guts = $mailer->getGuts();
+    $dict = ipull($guts['headers'], 1, 0);
+
+    if ($is_first_mail && $supports_message_id) {
+      $expect_message_id = true;
+      $expect_in_reply_to = false;
+      $expect_references = false;
+    } else {
+      $expect_message_id = false;
+      $expect_in_reply_to = true;
+      $expect_references = true;
+    }
+
+    $case = "<message-id = ".($supports_message_id ? 'Y' : 'N').", ".
+            "first = ".($is_first_mail ? 'Y' : 'N').">";
+
+    $this->assertEqual(
+      true,
+      isset($dict['Thread-Index']),
+      "Expect Thread-Index header for case {$case}.");
+    $this->assertEqual(
+      $expect_message_id,
+      isset($dict['Message-ID']),
+      "Expectation about existence of Message-ID header for case {$case}.");
+    $this->assertEqual(
+      $expect_in_reply_to,
+      isset($dict['In-Reply-To']),
+      "Expectation about existence of In-Reply-To header for case {$case}.");
+    $this->assertEqual(
+      $expect_references,
+      isset($dict['References']),
+      "Expectation about existence of References header for case {$case}.");
+  }
+
+}
diff --git a/src/applications/metamta/storage/mail/__tests__/__init__.php b/src/applications/metamta/storage/mail/__tests__/__init__.php
new file mode 100644
index 0000000000..b55a708549
--- /dev/null
+++ b/src/applications/metamta/storage/mail/__tests__/__init__.php
@@ -0,0 +1,16 @@
+<?php
+/**
+ * This file is automatically generated. Lint this module to rebuild it.
+ * @generated
+ */
+
+
+
+phutil_require_module('phabricator', 'applications/metamta/adapter/test');
+phutil_require_module('phabricator', 'applications/metamta/storage/mail');
+phutil_require_module('phabricator', 'infrastructure/testing/testcase');
+
+phutil_require_module('phutil', 'utils');
+
+
+phutil_require_source('PhabricatorMetaMTAMailTestCase.php');