diff --git a/scripts/daemon/exec/exec_daemon.php b/scripts/daemon/exec/exec_daemon.php
index 06620f9..e035bc2 100755
--- a/scripts/daemon/exec/exec_daemon.php
+++ b/scripts/daemon/exec/exec_daemon.php
@@ -1,113 +1,119 @@
 #!/usr/bin/env php
 <?php
 
 require_once dirname(__FILE__).'/../../__init_script__.php';
 
 if (!posix_isatty(STDOUT)) {
   $sid = posix_setsid();
   if ($sid <= 0) {
     throw new Exception('Failed to create new process session!');
   }
 }
 
 $args = new PhutilArgumentParser($argv);
 $args->setTagline('daemon executor');
 $args->setSynopsis(<<<EOHELP
 **exec_daemon.php** [__options__] __daemon__ ...
     Run an instance of __daemon__.
 EOHELP
   );
 $args->parse(
   array(
     array(
       'name' => 'trace',
       'help' => 'Enable debug tracing.',
     ),
     array(
       'name' => 'trace-memory',
       'help' => 'Enable debug memory tracing.',
     ),
     array(
       'name' => 'verbose',
       'help'  => 'Enable verbose activity logging.',
     ),
     array(
       'name' => 'label',
       'short' => 'l',
       'param' => 'label',
       'help' => pht(
         'Optional process label. Makes "ps" nicer, no behavioral effects.'),
     ),
     array(
       'name'     => 'daemon',
       'wildcard' => true,
     ),
   ));
 
 $trace_memory = $args->getArg('trace-memory');
 $trace_mode = $args->getArg('trace') || $trace_memory;
 $verbose = $args->getArg('verbose');
 
 if (function_exists('posix_isatty') && posix_isatty(STDIN)) {
   fprintf(STDERR, pht('Reading daemon configuration from stdin...')."\n");
 }
 $config = @file_get_contents('php://stdin');
 $config = id(new PhutilJSONParser())->parse($config);
 
 PhutilTypeSpec::checkMap(
   $config,
   array(
     'log' => 'optional string|null',
     'argv' => 'optional list<wild>',
     'load' => 'optional list<string>',
+    'autoscale' => 'optional wild',
   ));
 
 $log = idx($config, 'log');
 
 if ($log) {
   ini_set('error_log', $log);
   PhutilErrorHandler::setErrorListener(array('PhutilDaemon', 'errorListener'));
 }
 
 $load = idx($config, 'load', array());
 foreach ($load as $library) {
   $library = Filesystem::resolvePath($library);
   phutil_load_library($library);
 }
 
 PhutilErrorHandler::initialize();
 
 $daemon = $args->getArg('daemon');
 if (!$daemon) {
   throw new PhutilArgumentUsageException(
     pht('Specify which class of daemon to start.'));
 } else if (count($daemon) > 1) {
   throw new PhutilArgumentUsageException(
     pht('Specify exactly one daemon to start.'));
 } else {
   $daemon = head($daemon);
   if (!class_exists($daemon)) {
     throw new PhutilArgumentUsageException(
       pht('No class "%s" exists in any known library.', $daemon));
   } else if (!is_subclass_of($daemon, 'PhutilDaemon')) {
     throw new PhutilArgumentUsageException(
       pht('Class "%s" is not a subclass of "%s".', $daemon, 'PhutilDaemon'));
   }
 }
 
 $argv = idx($config, 'argv', array());
 $daemon = newv($daemon, array($argv));
 
 if ($trace_mode) {
   $daemon->setTraceMode();
 }
 
 if ($trace_memory) {
   $daemon->setTraceMemory();
 }
 
 if ($verbose) {
   $daemon->setVerbose(true);
 }
 
+$autoscale = idx($config, 'autoscale');
+if ($autoscale) {
+  $daemon->setAutoscaleProperties($autoscale);
+}
+
 $daemon->execute();
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
index f872a83..3c8c06d 100644
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -1,757 +1,759 @@
 <?php
 
 /**
  * This file is automatically generated. Use 'arc liberate' to rebuild it.
  *
  * @generated
  * @phutil-library-version 2
  */
 phutil_register_library_map(array(
   '__library_version__' => 2,
   'class' => array(
     'AASTNode' => 'parser/aast/api/AASTNode.php',
     'AASTNodeList' => 'parser/aast/api/AASTNodeList.php',
     'AASTToken' => 'parser/aast/api/AASTToken.php',
     'AASTTree' => 'parser/aast/api/AASTTree.php',
     'AbstractDirectedGraph' => 'utils/AbstractDirectedGraph.php',
     'AbstractDirectedGraphTestCase' => 'utils/__tests__/AbstractDirectedGraphTestCase.php',
     'AphrontAccessDeniedQueryException' => 'aphront/storage/exception/AphrontAccessDeniedQueryException.php',
     'AphrontBaseMySQLDatabaseConnection' => 'aphront/storage/connection/mysql/AphrontBaseMySQLDatabaseConnection.php',
     'AphrontCharacterSetQueryException' => 'aphront/storage/exception/AphrontCharacterSetQueryException.php',
     'AphrontConnectionLostQueryException' => 'aphront/storage/exception/AphrontConnectionLostQueryException.php',
     'AphrontConnectionQueryException' => 'aphront/storage/exception/AphrontConnectionQueryException.php',
     'AphrontCountQueryException' => 'aphront/storage/exception/AphrontCountQueryException.php',
     'AphrontDatabaseConnection' => 'aphront/storage/connection/AphrontDatabaseConnection.php',
     'AphrontDatabaseTransactionState' => 'aphront/storage/connection/AphrontDatabaseTransactionState.php',
     'AphrontDeadlockQueryException' => 'aphront/storage/exception/AphrontDeadlockQueryException.php',
     'AphrontDuplicateKeyQueryException' => 'aphront/storage/exception/AphrontDuplicateKeyQueryException.php',
     'AphrontIsolatedDatabaseConnection' => 'aphront/storage/connection/AphrontIsolatedDatabaseConnection.php',
     'AphrontMySQLDatabaseConnection' => 'aphront/storage/connection/mysql/AphrontMySQLDatabaseConnection.php',
     'AphrontMySQLiDatabaseConnection' => 'aphront/storage/connection/mysql/AphrontMySQLiDatabaseConnection.php',
     'AphrontNotSupportedQueryException' => 'aphront/storage/exception/AphrontNotSupportedQueryException.php',
     'AphrontObjectMissingQueryException' => 'aphront/storage/exception/AphrontObjectMissingQueryException.php',
     'AphrontParameterQueryException' => 'aphront/storage/exception/AphrontParameterQueryException.php',
     'AphrontQueryException' => 'aphront/storage/exception/AphrontQueryException.php',
     'AphrontRecoverableQueryException' => 'aphront/storage/exception/AphrontRecoverableQueryException.php',
     'AphrontSchemaQueryException' => 'aphront/storage/exception/AphrontSchemaQueryException.php',
     'AphrontScopedUnguardedWriteCapability' => 'aphront/writeguard/AphrontScopedUnguardedWriteCapability.php',
     'AphrontWriteGuard' => 'aphront/writeguard/AphrontWriteGuard.php',
     'AphrontWriteGuardExitEventListener' => 'aphront/writeguard/event/AphrontWriteGuardExitEventListener.php',
     'BaseHTTPFuture' => 'future/http/BaseHTTPFuture.php',
     'CommandException' => 'future/exec/CommandException.php',
     'ConduitClient' => 'conduit/ConduitClient.php',
     'ConduitClientException' => 'conduit/ConduitClientException.php',
     'ConduitClientTestCase' => 'conduit/__tests__/ConduitClientTestCase.php',
     'ConduitFuture' => 'conduit/ConduitFuture.php',
     'ExecFuture' => 'future/exec/ExecFuture.php',
     'ExecFutureTestCase' => 'future/exec/__tests__/ExecFutureTestCase.php',
     'ExecPassthruTestCase' => 'future/exec/__tests__/ExecPassthruTestCase.php',
     'FileFinder' => 'filesystem/FileFinder.php',
     'FileFinderTestCase' => 'filesystem/__tests__/FileFinderTestCase.php',
     'FileList' => 'filesystem/FileList.php',
     'Filesystem' => 'filesystem/Filesystem.php',
     'FilesystemException' => 'filesystem/FilesystemException.php',
     'FilesystemTestCase' => 'filesystem/__tests__/FilesystemTestCase.php',
     'Future' => 'future/Future.php',
     'FutureIterator' => 'future/FutureIterator.php',
     'FutureIteratorTestCase' => 'future/__tests__/FutureIteratorTestCase.php',
     'FutureProxy' => 'future/FutureProxy.php',
     'HTTPFuture' => 'future/http/HTTPFuture.php',
     'HTTPFutureCURLResponseStatus' => 'future/http/status/HTTPFutureCURLResponseStatus.php',
     'HTTPFutureCertificateResponseStatus' => 'future/http/status/HTTPFutureCertificateResponseStatus.php',
     'HTTPFutureHTTPResponseStatus' => 'future/http/status/HTTPFutureHTTPResponseStatus.php',
     'HTTPFutureParseResponseStatus' => 'future/http/status/HTTPFutureParseResponseStatus.php',
     'HTTPFutureResponseStatus' => 'future/http/status/HTTPFutureResponseStatus.php',
     'HTTPFutureTransportResponseStatus' => 'future/http/status/HTTPFutureTransportResponseStatus.php',
     'HTTPSFuture' => 'future/http/HTTPSFuture.php',
     'ImmediateFuture' => 'future/ImmediateFuture.php',
     'LinesOfALarge' => 'filesystem/linesofalarge/LinesOfALarge.php',
     'LinesOfALargeExecFuture' => 'filesystem/linesofalarge/LinesOfALargeExecFuture.php',
     'LinesOfALargeExecFutureTestCase' => 'filesystem/linesofalarge/__tests__/LinesOfALargeExecFutureTestCase.php',
     'LinesOfALargeFile' => 'filesystem/linesofalarge/LinesOfALargeFile.php',
     'LinesOfALargeFileTestCase' => 'filesystem/linesofalarge/__tests__/LinesOfALargeFileTestCase.php',
     'MFilterTestHelper' => 'utils/__tests__/MFilterTestHelper.php',
     'PHPASTParserTestCase' => 'parser/xhpast/__tests__/PHPASTParserTestCase.php',
     'PhageAgentBootloader' => 'phage/bootloader/PhageAgentBootloader.php',
     'PhageAgentTestCase' => 'phage/__tests__/PhageAgentTestCase.php',
     'PhagePHPAgent' => 'phage/agent/PhagePHPAgent.php',
     'PhagePHPAgentBootloader' => 'phage/bootloader/PhagePHPAgentBootloader.php',
     'Phobject' => 'object/Phobject.php',
     'PhutilAPCKeyValueCache' => 'cache/PhutilAPCKeyValueCache.php',
     'PhutilAWSEC2Future' => 'future/aws/PhutilAWSEC2Future.php',
     'PhutilAWSException' => 'future/aws/PhutilAWSException.php',
     'PhutilAWSFuture' => 'future/aws/PhutilAWSFuture.php',
     'PhutilAWSS3Future' => 'future/aws/PhutilAWSS3Future.php',
     'PhutilAggregateException' => 'error/PhutilAggregateException.php',
     'PhutilAllCapsEnglishLocale' => 'internationalization/locales/PhutilAllCapsEnglishLocale.php',
     'PhutilAmazonAuthAdapter' => 'auth/PhutilAmazonAuthAdapter.php',
     'PhutilArgumentParser' => 'parser/argument/PhutilArgumentParser.php',
     'PhutilArgumentParserException' => 'parser/argument/exception/PhutilArgumentParserException.php',
     'PhutilArgumentParserTestCase' => 'parser/argument/__tests__/PhutilArgumentParserTestCase.php',
     'PhutilArgumentSpecification' => 'parser/argument/PhutilArgumentSpecification.php',
     'PhutilArgumentSpecificationException' => 'parser/argument/exception/PhutilArgumentSpecificationException.php',
     'PhutilArgumentSpecificationTestCase' => 'parser/argument/__tests__/PhutilArgumentSpecificationTestCase.php',
     'PhutilArgumentUsageException' => 'parser/argument/exception/PhutilArgumentUsageException.php',
     'PhutilArgumentWorkflow' => 'parser/argument/workflow/PhutilArgumentWorkflow.php',
     'PhutilArray' => 'utils/PhutilArray.php',
     'PhutilArrayTestCase' => 'utils/__tests__/PhutilArrayTestCase.php',
     'PhutilArrayWithDefaultValue' => 'utils/PhutilArrayWithDefaultValue.php',
     'PhutilAsanaAuthAdapter' => 'auth/PhutilAsanaAuthAdapter.php',
     'PhutilAsanaFuture' => 'future/asana/PhutilAsanaFuture.php',
     'PhutilAuthAdapter' => 'auth/PhutilAuthAdapter.php',
     'PhutilAuthConfigurationException' => 'auth/exception/PhutilAuthConfigurationException.php',
     'PhutilAuthCredentialException' => 'auth/exception/PhutilAuthCredentialException.php',
     'PhutilAuthException' => 'auth/exception/PhutilAuthException.php',
     'PhutilAuthUserAbortedException' => 'auth/exception/PhutilAuthUserAbortedException.php',
     'PhutilBallOfPHP' => 'phage/util/PhutilBallOfPHP.php',
     'PhutilBitbucketAuthAdapter' => 'auth/PhutilBitbucketAuthAdapter.php',
     'PhutilBootloader' => 'moduleutils/PhutilBootloader.php',
     'PhutilBootloaderException' => 'moduleutils/PhutilBootloaderException.php',
     'PhutilBritishEnglishLocale' => 'internationalization/locales/PhutilBritishEnglishLocale.php',
     'PhutilBufferedIterator' => 'utils/PhutilBufferedIterator.php',
     'PhutilBufferedIteratorTestCase' => 'utils/__tests__/PhutilBufferedIteratorTestCase.php',
     'PhutilBugtraqParser' => 'parser/PhutilBugtraqParser.php',
     'PhutilBugtraqParserTestCase' => 'parser/__tests__/PhutilBugtraqParserTestCase.php',
     'PhutilCIDRBlock' => 'ip/PhutilCIDRBlock.php',
     'PhutilCIDRList' => 'ip/PhutilCIDRList.php',
     'PhutilCLikeCodeSnippetContextFreeGrammar' => 'grammar/code/PhutilCLikeCodeSnippetContextFreeGrammar.php',
     'PhutilCallbackFilterIterator' => 'utils/PhutilCallbackFilterIterator.php',
     'PhutilChannel' => 'channel/PhutilChannel.php',
     'PhutilChannelChannel' => 'channel/PhutilChannelChannel.php',
     'PhutilChannelTestCase' => 'channel/__tests__/PhutilChannelTestCase.php',
     'PhutilChunkedIterator' => 'utils/PhutilChunkedIterator.php',
     'PhutilChunkedIteratorTestCase' => 'utils/__tests__/PhutilChunkedIteratorTestCase.php',
     'PhutilCodeSnippetContextFreeGrammar' => 'grammar/code/PhutilCodeSnippetContextFreeGrammar.php',
     'PhutilCommandString' => 'xsprintf/PhutilCommandString.php',
     'PhutilConsole' => 'console/PhutilConsole.php',
     'PhutilConsoleFormatter' => 'console/PhutilConsoleFormatter.php',
     'PhutilConsoleMessage' => 'console/PhutilConsoleMessage.php',
     'PhutilConsoleProgressBar' => 'console/PhutilConsoleProgressBar.php',
     'PhutilConsoleServer' => 'console/PhutilConsoleServer.php',
     'PhutilConsoleServerChannel' => 'console/PhutilConsoleServerChannel.php',
     'PhutilConsoleStdinNotInteractiveException' => 'console/PhutilConsoleStdinNotInteractiveException.php',
     'PhutilConsoleSyntaxHighlighter' => 'markup/syntax/highlighter/PhutilConsoleSyntaxHighlighter.php',
     'PhutilConsoleTable' => 'console/PhutilConsoleTable.php',
     'PhutilConsoleWrapTestCase' => 'console/__tests__/PhutilConsoleWrapTestCase.php',
     'PhutilContextFreeGrammar' => 'grammar/PhutilContextFreeGrammar.php',
     'PhutilCsprintfTestCase' => 'xsprintf/__tests__/PhutilCsprintfTestCase.php',
     'PhutilCzechLocale' => 'internationalization/locales/PhutilCzechLocale.php',
     'PhutilDaemon' => 'daemon/PhutilDaemon.php',
     'PhutilDaemonHandle' => 'daemon/PhutilDaemonHandle.php',
     'PhutilDaemonOverseer' => 'daemon/PhutilDaemonOverseer.php',
     'PhutilDefaultSyntaxHighlighter' => 'markup/syntax/highlighter/PhutilDefaultSyntaxHighlighter.php',
     'PhutilDefaultSyntaxHighlighterEngine' => 'markup/syntax/engine/PhutilDefaultSyntaxHighlighterEngine.php',
     'PhutilDefaultSyntaxHighlighterEnginePygmentsFuture' => 'markup/syntax/highlighter/pygments/PhutilDefaultSyntaxHighlighterEnginePygmentsFuture.php',
     'PhutilDefaultSyntaxHighlighterEngineTestCase' => 'markup/syntax/engine/__tests__/PhutilDefaultSyntaxHighlighterEngineTestCase.php',
     'PhutilDeferredLog' => 'filesystem/PhutilDeferredLog.php',
     'PhutilDeferredLogTestCase' => 'filesystem/__tests__/PhutilDeferredLogTestCase.php',
     'PhutilDirectedScalarGraph' => 'utils/PhutilDirectedScalarGraph.php',
     'PhutilDirectoryFixture' => 'filesystem/PhutilDirectoryFixture.php',
     'PhutilDirectoryKeyValueCache' => 'cache/PhutilDirectoryKeyValueCache.php',
     'PhutilDisqusAuthAdapter' => 'auth/PhutilDisqusAuthAdapter.php',
     'PhutilDivinerSyntaxHighlighter' => 'markup/syntax/highlighter/PhutilDivinerSyntaxHighlighter.php',
     'PhutilDocblockParser' => 'parser/PhutilDocblockParser.php',
     'PhutilDocblockParserTestCase' => 'parser/__tests__/PhutilDocblockParserTestCase.php',
     'PhutilEditDistanceMatrix' => 'utils/PhutilEditDistanceMatrix.php',
     'PhutilEditDistanceMatrixTestCase' => 'utils/__tests__/PhutilEditDistanceMatrixTestCase.php',
     'PhutilEmailAddress' => 'parser/PhutilEmailAddress.php',
     'PhutilEmailAddressTestCase' => 'parser/__tests__/PhutilEmailAddressTestCase.php',
     'PhutilEmptyAuthAdapter' => 'auth/PhutilEmptyAuthAdapter.php',
     'PhutilErrorHandler' => 'error/PhutilErrorHandler.php',
     'PhutilErrorHandlerTestCase' => 'error/__tests__/PhutilErrorHandlerTestCase.php',
     'PhutilErrorTrap' => 'error/PhutilErrorTrap.php',
     'PhutilEvent' => 'events/PhutilEvent.php',
     'PhutilEventConstants' => 'events/constant/PhutilEventConstants.php',
     'PhutilEventEngine' => 'events/PhutilEventEngine.php',
     'PhutilEventListener' => 'events/PhutilEventListener.php',
     'PhutilEventType' => 'events/constant/PhutilEventType.php',
     'PhutilExampleBufferedIterator' => 'utils/PhutilExampleBufferedIterator.php',
     'PhutilExcessiveServiceCallsDaemon' => 'daemon/torture/PhutilExcessiveServiceCallsDaemon.php',
     'PhutilExecChannel' => 'channel/PhutilExecChannel.php',
     'PhutilExecPassthru' => 'future/exec/PhutilExecPassthru.php',
     'PhutilExecutionEnvironment' => 'utils/PhutilExecutionEnvironment.php',
     'PhutilExtensionsTestCase' => 'moduleutils/__tests__/PhutilExtensionsTestCase.php',
     'PhutilFacebookAuthAdapter' => 'auth/PhutilFacebookAuthAdapter.php',
     'PhutilFatalDaemon' => 'daemon/torture/PhutilFatalDaemon.php',
     'PhutilFileLock' => 'filesystem/PhutilFileLock.php',
     'PhutilFileLockTestCase' => 'filesystem/__tests__/PhutilFileLockTestCase.php',
     'PhutilFileTree' => 'filesystem/PhutilFileTree.php',
     'PhutilGitHubAuthAdapter' => 'auth/PhutilGitHubAuthAdapter.php',
     'PhutilGitURI' => 'parser/PhutilGitURI.php',
     'PhutilGitURITestCase' => 'parser/__tests__/PhutilGitURITestCase.php',
     'PhutilGoogleAuthAdapter' => 'auth/PhutilGoogleAuthAdapter.php',
     'PhutilHangForeverDaemon' => 'daemon/torture/PhutilHangForeverDaemon.php',
     'PhutilHelpArgumentWorkflow' => 'parser/argument/workflow/PhutilHelpArgumentWorkflow.php',
     'PhutilHgsprintfTestCase' => 'xsprintf/__tests__/PhutilHgsprintfTestCase.php',
+    'PhutilHighIntensityIntervalDaemon' => 'daemon/torture/PhutilHighIntensityIntervalDaemon.php',
     'PhutilINIParserException' => 'parser/exception/PhutilINIParserException.php',
     'PhutilIPAddress' => 'ip/PhutilIPAddress.php',
     'PhutilIPAddressTestCase' => 'ip/__tests__/PhutilIPAddressTestCase.php',
     'PhutilInRequestKeyValueCache' => 'cache/PhutilInRequestKeyValueCache.php',
     'PhutilInteractiveEditor' => 'console/PhutilInteractiveEditor.php',
     'PhutilInvalidRuleParserGeneratorException' => 'parser/generator/exception/PhutilInvalidRuleParserGeneratorException.php',
     'PhutilInvisibleSyntaxHighlighter' => 'markup/syntax/highlighter/PhutilInvisibleSyntaxHighlighter.php',
     'PhutilIrreducibleRuleParserGeneratorException' => 'parser/generator/exception/PhutilIrreducibleRuleParserGeneratorException.php',
     'PhutilJIRAAuthAdapter' => 'auth/PhutilJIRAAuthAdapter.php',
     'PhutilJSON' => 'parser/PhutilJSON.php',
     'PhutilJSONParser' => 'parser/PhutilJSONParser.php',
     'PhutilJSONParserException' => 'parser/exception/PhutilJSONParserException.php',
     'PhutilJSONParserTestCase' => 'parser/__tests__/PhutilJSONParserTestCase.php',
     'PhutilJSONProtocolChannel' => 'channel/PhutilJSONProtocolChannel.php',
     'PhutilJSONProtocolChannelTestCase' => 'channel/__tests__/PhutilJSONProtocolChannelTestCase.php',
     'PhutilJSONTestCase' => 'parser/__tests__/PhutilJSONTestCase.php',
     'PhutilJavaCodeSnippetContextFreeGrammar' => 'grammar/code/PhutilJavaCodeSnippetContextFreeGrammar.php',
     'PhutilKeyValueCache' => 'cache/PhutilKeyValueCache.php',
     'PhutilKeyValueCacheNamespace' => 'cache/PhutilKeyValueCacheNamespace.php',
     'PhutilKeyValueCacheProfiler' => 'cache/PhutilKeyValueCacheProfiler.php',
     'PhutilKeyValueCacheProxy' => 'cache/PhutilKeyValueCacheProxy.php',
     'PhutilKeyValueCacheStack' => 'cache/PhutilKeyValueCacheStack.php',
     'PhutilKeyValueCacheTestCase' => 'cache/__tests__/PhutilKeyValueCacheTestCase.php',
     'PhutilLDAPAuthAdapter' => 'auth/PhutilLDAPAuthAdapter.php',
     'PhutilLanguageGuesser' => 'parser/PhutilLanguageGuesser.php',
     'PhutilLanguageGuesserTestCase' => 'parser/__tests__/PhutilLanguageGuesserTestCase.php',
     'PhutilLexer' => 'lexer/PhutilLexer.php',
     'PhutilLexerSyntaxHighlighter' => 'markup/syntax/highlighter/PhutilLexerSyntaxHighlighter.php',
     'PhutilLibraryConflictException' => 'moduleutils/PhutilLibraryConflictException.php',
     'PhutilLibraryMapBuilder' => 'moduleutils/PhutilLibraryMapBuilder.php',
     'PhutilLibraryTestCase' => '__tests__/PhutilLibraryTestCase.php',
     'PhutilLipsumContextFreeGrammar' => 'grammar/PhutilLipsumContextFreeGrammar.php',
     'PhutilLocale' => 'internationalization/PhutilLocale.php',
     'PhutilLock' => 'filesystem/PhutilLock.php',
     'PhutilLockException' => 'filesystem/PhutilLockException.php',
     'PhutilLogFileChannel' => 'channel/PhutilLogFileChannel.php',
     'PhutilLunarPhase' => 'utils/PhutilLunarPhase.php',
     'PhutilLunarPhaseTestCase' => 'utils/__tests__/PhutilLunarPhaseTestCase.php',
     'PhutilMarkupEngine' => 'markup/PhutilMarkupEngine.php',
     'PhutilMarkupTestCase' => 'markup/__tests__/PhutilMarkupTestCase.php',
     'PhutilMemcacheKeyValueCache' => 'cache/PhutilMemcacheKeyValueCache.php',
     'PhutilMethodNotImplementedException' => 'error/PhutilMethodNotImplementedException.php',
     'PhutilMetricsChannel' => 'channel/PhutilMetricsChannel.php',
     'PhutilMissingSymbolException' => 'symbols/exception/PhutilMissingSymbolException.php',
     'PhutilModuleUtilsTestCase' => 'moduleutils/__tests__/PhutilModuleUtilsTestCase.php',
     'PhutilNiceDaemon' => 'daemon/torture/PhutilNiceDaemon.php',
     'PhutilNumber' => 'internationalization/PhutilNumber.php',
     'PhutilOAuth1AuthAdapter' => 'auth/PhutilOAuth1AuthAdapter.php',
     'PhutilOAuth1Future' => 'future/oauth/PhutilOAuth1Future.php',
     'PhutilOAuth1FutureTestCase' => 'future/oauth/__tests__/PhutilOAuth1FutureTestCase.php',
     'PhutilOAuthAuthAdapter' => 'auth/PhutilOAuthAuthAdapter.php',
     'PhutilOnDiskKeyValueCache' => 'cache/PhutilOnDiskKeyValueCache.php',
     'PhutilOpaqueEnvelope' => 'error/PhutilOpaqueEnvelope.php',
     'PhutilOpaqueEnvelopeKey' => 'error/PhutilOpaqueEnvelopeKey.php',
     'PhutilOpaqueEnvelopeTestCase' => 'error/__tests__/PhutilOpaqueEnvelopeTestCase.php',
     'PhutilPHPCodeSnippetContextFreeGrammar' => 'grammar/code/PhutilPHPCodeSnippetContextFreeGrammar.php',
     'PhutilPHPFragmentLexer' => 'lexer/PhutilPHPFragmentLexer.php',
     'PhutilPHPFragmentLexerHighlighterTestCase' => 'markup/syntax/highlighter/__tests__/PhutilPHPFragmentLexerHighlighterTestCase.php',
     'PhutilPHPFragmentLexerTestCase' => 'lexer/__tests__/PhutilPHPFragmentLexerTestCase.php',
     'PhutilPHPObjectProtocolChannel' => 'channel/PhutilPHPObjectProtocolChannel.php',
     'PhutilPHPObjectProtocolChannelTestCase' => 'channel/__tests__/PhutilPHPObjectProtocolChannelTestCase.php',
     'PhutilParserGenerator' => 'parser/PhutilParserGenerator.php',
     'PhutilParserGeneratorException' => 'parser/generator/exception/PhutilParserGeneratorException.php',
     'PhutilParserGeneratorTestCase' => 'parser/__tests__/PhutilParserGeneratorTestCase.php',
     'PhutilPayPalAPIFuture' => 'future/paypal/PhutilPayPalAPIFuture.php',
     'PhutilPerson' => 'internationalization/PhutilPerson.php',
     'PhutilPersonTest' => 'internationalization/__tests__/PhutilPersonTest.php',
     'PhutilPersonaAuthAdapter' => 'auth/PhutilPersonaAuthAdapter.php',
     'PhutilPhabricatorAuthAdapter' => 'auth/PhutilPhabricatorAuthAdapter.php',
     'PhutilPhobjectTestCase' => 'object/__tests__/PhutilPhobjectTestCase.php',
     'PhutilPhtTestCase' => 'internationalization/__tests__/PhutilPhtTestCase.php',
     'PhutilProcessGroupDaemon' => 'daemon/torture/PhutilProcessGroupDaemon.php',
     'PhutilProtocolChannel' => 'channel/PhutilProtocolChannel.php',
     'PhutilProxyException' => 'error/PhutilProxyException.php',
     'PhutilPygmentsSyntaxHighlighter' => 'markup/syntax/highlighter/PhutilPygmentsSyntaxHighlighter.php',
     'PhutilPythonFragmentLexer' => 'lexer/PhutilPythonFragmentLexer.php',
     'PhutilQsprintfInterface' => 'xsprintf/PhutilQsprintfInterface.php',
     'PhutilQueryStringParser' => 'parser/PhutilQueryStringParser.php',
     'PhutilQueryStringParserTestCase' => 'parser/__tests__/PhutilQueryStringParserTestCase.php',
     'PhutilRainbowSyntaxHighlighter' => 'markup/syntax/highlighter/PhutilRainbowSyntaxHighlighter.php',
     'PhutilRawEnglishLocale' => 'internationalization/locales/PhutilRawEnglishLocale.php',
     'PhutilReadableSerializer' => 'readableserializer/PhutilReadableSerializer.php',
     'PhutilReadableSerializerTestCase' => 'readableserializer/__tests__/PhutilReadableSerializerTestCase.php',
     'PhutilRealNameContextFreeGrammar' => 'grammar/PhutilRealNameContextFreeGrammar.php',
     'PhutilRemarkupBlockInterpreter' => 'markup/engine/remarkup/blockrule/PhutilRemarkupBlockInterpreter.php',
     'PhutilRemarkupBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupBlockRule.php',
     'PhutilRemarkupBlockStorage' => 'markup/engine/remarkup/PhutilRemarkupBlockStorage.php',
     'PhutilRemarkupBoldRule' => 'markup/engine/remarkup/markuprule/PhutilRemarkupBoldRule.php',
     'PhutilRemarkupCodeBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupCodeBlockRule.php',
     'PhutilRemarkupDefaultBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupDefaultBlockRule.php',
     'PhutilRemarkupDelRule' => 'markup/engine/remarkup/markuprule/PhutilRemarkupDelRule.php',
     'PhutilRemarkupDocumentLinkRule' => 'markup/engine/remarkup/markuprule/PhutilRemarkupDocumentLinkRule.php',
     'PhutilRemarkupEngine' => 'markup/engine/PhutilRemarkupEngine.php',
     'PhutilRemarkupEngineTestCase' => 'markup/engine/__tests__/PhutilRemarkupEngineTestCase.php',
     'PhutilRemarkupEscapeRemarkupRule' => 'markup/engine/remarkup/markuprule/PhutilRemarkupEscapeRemarkupRule.php',
     'PhutilRemarkupHeaderBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupHeaderBlockRule.php',
     'PhutilRemarkupHorizontalRuleBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupHorizontalRuleBlockRule.php',
     'PhutilRemarkupHyperlinkRule' => 'markup/engine/remarkup/markuprule/PhutilRemarkupHyperlinkRule.php',
     'PhutilRemarkupInlineBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupInlineBlockRule.php',
     'PhutilRemarkupInterpreterBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupInterpreterBlockRule.php',
     'PhutilRemarkupItalicRule' => 'markup/engine/remarkup/markuprule/PhutilRemarkupItalicRule.php',
     'PhutilRemarkupLinebreaksRule' => 'markup/engine/remarkup/markuprule/PhutilRemarkupLinebreaksRule.php',
     'PhutilRemarkupListBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupListBlockRule.php',
     'PhutilRemarkupLiteralBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupLiteralBlockRule.php',
     'PhutilRemarkupMonospaceRule' => 'markup/engine/remarkup/markuprule/PhutilRemarkupMonospaceRule.php',
     'PhutilRemarkupNoteBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupNoteBlockRule.php',
     'PhutilRemarkupQuotesBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupQuotesBlockRule.php',
     'PhutilRemarkupReplyBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupReplyBlockRule.php',
     'PhutilRemarkupRule' => 'markup/engine/remarkup/markuprule/PhutilRemarkupRule.php',
     'PhutilRemarkupSimpleTableBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupSimpleTableBlockRule.php',
     'PhutilRemarkupTableBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupTableBlockRule.php',
     'PhutilRemarkupTestInterpreterRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupTestInterpreterRule.php',
     'PhutilRemarkupUnderlineRule' => 'markup/engine/remarkup/markuprule/PhutilRemarkupUnderlineRule.php',
     'PhutilRope' => 'utils/PhutilRope.php',
     'PhutilRopeTestCase' => 'utils/__tests__/PhutilRopeTestCase.php',
     'PhutilSafeHTML' => 'markup/PhutilSafeHTML.php',
     'PhutilSafeHTMLProducerInterface' => 'markup/PhutilSafeHTMLProducerInterface.php',
     'PhutilSafeHTMLTestCase' => 'markup/__tests__/PhutilSafeHTMLTestCase.php',
     'PhutilSaturateStdoutDaemon' => 'daemon/torture/PhutilSaturateStdoutDaemon.php',
     'PhutilServiceProfiler' => 'serviceprofiler/PhutilServiceProfiler.php',
     'PhutilShellLexer' => 'lexer/PhutilShellLexer.php',
     'PhutilShellLexerTestCase' => 'lexer/__tests__/PhutilShellLexerTestCase.php',
     'PhutilSimpleOptions' => 'parser/PhutilSimpleOptions.php',
     'PhutilSimpleOptionsLexer' => 'lexer/PhutilSimpleOptionsLexer.php',
     'PhutilSimpleOptionsLexerTestCase' => 'lexer/__tests__/PhutilSimpleOptionsLexerTestCase.php',
     'PhutilSimpleOptionsTestCase' => 'parser/__tests__/PhutilSimpleOptionsTestCase.php',
     'PhutilSocketChannel' => 'channel/PhutilSocketChannel.php',
     'PhutilSprite' => 'sprites/PhutilSprite.php',
     'PhutilSpriteSheet' => 'sprites/PhutilSpriteSheet.php',
     'PhutilSymbolLoader' => 'symbols/PhutilSymbolLoader.php',
     'PhutilSyntaxHighlighter' => 'markup/syntax/highlighter/PhutilSyntaxHighlighter.php',
     'PhutilSyntaxHighlighterEngine' => 'markup/syntax/engine/PhutilSyntaxHighlighterEngine.php',
     'PhutilSyntaxHighlighterException' => 'markup/syntax/highlighter/PhutilSyntaxHighlighterException.php',
     'PhutilSystem' => 'utils/PhutilSystem.php',
     'PhutilSystemTestCase' => 'utils/__tests__/PhutilSystemTestCase.php',
     'PhutilTestCase' => 'infrastructure/testing/PhutilTestCase.php',
     'PhutilTestPhobject' => 'object/__tests__/PhutilTestPhobject.php',
     'PhutilTortureTestDaemon' => 'daemon/torture/PhutilTortureTestDaemon.php',
     'PhutilTranslation' => 'internationalization/PhutilTranslation.php',
     'PhutilTranslator' => 'internationalization/PhutilTranslator.php',
     'PhutilTranslatorTestCase' => 'internationalization/__tests__/PhutilTranslatorTestCase.php',
     'PhutilTwitchAuthAdapter' => 'auth/PhutilTwitchAuthAdapter.php',
     'PhutilTwitchFuture' => 'future/twitch/PhutilTwitchFuture.php',
     'PhutilTwitterAuthAdapter' => 'auth/PhutilTwitterAuthAdapter.php',
     'PhutilTypeCheckException' => 'parser/exception/PhutilTypeCheckException.php',
     'PhutilTypeExtraParametersException' => 'parser/exception/PhutilTypeExtraParametersException.php',
     'PhutilTypeLexer' => 'lexer/PhutilTypeLexer.php',
     'PhutilTypeMissingParametersException' => 'parser/exception/PhutilTypeMissingParametersException.php',
     'PhutilTypeSpec' => 'parser/PhutilTypeSpec.php',
     'PhutilTypeSpecTestCase' => 'parser/__tests__/PhutilTypeSpecTestCase.php',
     'PhutilURI' => 'parser/PhutilURI.php',
     'PhutilURITestCase' => 'parser/__tests__/PhutilURITestCase.php',
     'PhutilUSEnglishLocale' => 'internationalization/locales/PhutilUSEnglishLocale.php',
     'PhutilUTF8StringTruncator' => 'utils/PhutilUTF8StringTruncator.php',
     'PhutilUTF8TestCase' => 'utils/__tests__/PhutilUTF8TestCase.php',
     'PhutilUnknownSymbolParserGeneratorException' => 'parser/generator/exception/PhutilUnknownSymbolParserGeneratorException.php',
     'PhutilUnreachableRuleParserGeneratorException' => 'parser/generator/exception/PhutilUnreachableRuleParserGeneratorException.php',
     'PhutilUnreachableTerminalParserGeneratorException' => 'parser/generator/exception/PhutilUnreachableTerminalParserGeneratorException.php',
     'PhutilUrisprintfTestCase' => 'xsprintf/__tests__/PhutilUrisprintfTestCase.php',
     'PhutilUtilsTestCase' => 'utils/__tests__/PhutilUtilsTestCase.php',
     'PhutilVeryWowEnglishLocale' => 'internationalization/locales/PhutilVeryWowEnglishLocale.php',
     'PhutilWordPressAuthAdapter' => 'auth/PhutilWordPressAuthAdapter.php',
     'PhutilWordPressFuture' => 'future/wordpress/PhutilWordPressFuture.php',
     'PhutilXHPASTBinary' => 'parser/xhpast/bin/PhutilXHPASTBinary.php',
     'PhutilXHPASTSyntaxHighlighter' => 'markup/syntax/highlighter/PhutilXHPASTSyntaxHighlighter.php',
     'PhutilXHPASTSyntaxHighlighterFuture' => 'markup/syntax/highlighter/xhpast/PhutilXHPASTSyntaxHighlighterFuture.php',
     'PhutilXHPASTSyntaxHighlighterTestCase' => 'markup/syntax/highlighter/__tests__/PhutilXHPASTSyntaxHighlighterTestCase.php',
     'QueryFuture' => 'future/query/QueryFuture.php',
     'TempFile' => 'filesystem/TempFile.php',
     'TestAbstractDirectedGraph' => 'utils/__tests__/TestAbstractDirectedGraph.php',
     'XHPASTNode' => 'parser/xhpast/api/XHPASTNode.php',
     'XHPASTNodeTestCase' => 'parser/xhpast/api/__tests__/XHPASTNodeTestCase.php',
     'XHPASTSyntaxErrorException' => 'parser/xhpast/api/XHPASTSyntaxErrorException.php',
     'XHPASTToken' => 'parser/xhpast/api/XHPASTToken.php',
     'XHPASTTree' => 'parser/xhpast/api/XHPASTTree.php',
     'XHPASTTreeTestCase' => 'parser/xhpast/api/__tests__/XHPASTTreeTestCase.php',
     'XsprintfUnknownConversionException' => 'xsprintf/exception/XsprintfUnknownConversionException.php',
   ),
   'function' => array(
     '_qsprintf_check_scalar_type' => 'xsprintf/qsprintf.php',
     '_qsprintf_check_type' => 'xsprintf/qsprintf.php',
     'array_fuse' => 'utils/utils.php',
     'array_interleave' => 'utils/utils.php',
     'array_mergev' => 'utils/utils.php',
     'array_select_keys' => 'utils/utils.php',
     'assert_instances_of' => 'utils/utils.php',
     'assert_stringlike' => 'utils/utils.php',
     'coalesce' => 'utils/utils.php',
     'csprintf' => 'xsprintf/csprintf.php',
     'exec_manual' => 'future/exec/execx.php',
     'execx' => 'future/exec/execx.php',
     'head' => 'utils/utils.php',
     'head_key' => 'utils/utils.php',
     'hgsprintf' => 'xsprintf/hgsprintf.php',
     'hsprintf' => 'markup/render.php',
     'id' => 'utils/utils.php',
     'idx' => 'utils/utils.php',
     'ifilter' => 'utils/utils.php',
     'igroup' => 'utils/utils.php',
     'ipull' => 'utils/utils.php',
     'isort' => 'utils/utils.php',
     'jsprintf' => 'xsprintf/jsprintf.php',
     'last' => 'utils/utils.php',
     'last_key' => 'utils/utils.php',
     'ldap_sprintf' => 'xsprintf/ldapsprintf.php',
     'mfilter' => 'utils/utils.php',
     'mgroup' => 'utils/utils.php',
     'mpull' => 'utils/utils.php',
     'msort' => 'utils/utils.php',
     'newv' => 'utils/utils.php',
     'nonempty' => 'utils/utils.php',
     'phlog' => 'error/phlog.php',
     'pht' => 'internationalization/pht.php',
     'phutil_censor_credentials' => 'utils/utils.php',
     'phutil_console_confirm' => 'console/format.php',
     'phutil_console_format' => 'console/format.php',
     'phutil_console_get_terminal_width' => 'console/format.php',
     'phutil_console_prompt' => 'console/format.php',
     'phutil_console_require_tty' => 'console/format.php',
     'phutil_console_wrap' => 'console/format.php',
     'phutil_date_format' => 'utils/viewutils.php',
     'phutil_deprecated' => 'moduleutils/moduleutils.php',
     'phutil_error_listener_example' => 'error/phlog.php',
     'phutil_escape_html' => 'markup/render.php',
     'phutil_escape_html_newlines' => 'markup/render.php',
     'phutil_escape_uri' => 'markup/render.php',
     'phutil_escape_uri_path_component' => 'markup/render.php',
     'phutil_exit' => 'utils/utils.php',
     'phutil_fnmatch' => 'utils/utils.php',
     'phutil_format_bytes' => 'utils/viewutils.php',
     'phutil_format_relative_time' => 'utils/viewutils.php',
     'phutil_format_relative_time_detailed' => 'utils/viewutils.php',
     'phutil_format_units_generic' => 'utils/viewutils.php',
     'phutil_fwrite_nonblocking_stream' => 'utils/utils.php',
     'phutil_get_current_library_name' => 'moduleutils/moduleutils.php',
     'phutil_get_library_name_for_root' => 'moduleutils/moduleutils.php',
     'phutil_get_library_root' => 'moduleutils/moduleutils.php',
     'phutil_get_library_root_for_path' => 'moduleutils/moduleutils.php',
     'phutil_get_signal_name' => 'future/exec/execx.php',
     'phutil_implode_html' => 'markup/render.php',
     'phutil_ini_decode' => 'utils/utils.php',
     'phutil_is_hiphop_runtime' => 'utils/utils.php',
     'phutil_is_utf8' => 'utils/utf8.php',
     'phutil_is_utf8_slowly' => 'utils/utf8.php',
     'phutil_is_utf8_with_only_bmp_characters' => 'utils/utf8.php',
     'phutil_is_windows' => 'utils/utils.php',
     'phutil_json_decode' => 'utils/utils.php',
     'phutil_load_library' => 'moduleutils/core.php',
     'phutil_loggable_string' => 'utils/utils.php',
     'phutil_parse_bytes' => 'utils/viewutils.php',
     'phutil_passthru' => 'future/exec/execx.php',
     'phutil_register_library' => 'moduleutils/core.php',
     'phutil_register_library_map' => 'moduleutils/core.php',
     'phutil_safe_html' => 'markup/render.php',
     'phutil_split_lines' => 'utils/utils.php',
     'phutil_tag' => 'markup/render.php',
     'phutil_tag_div' => 'markup/render.php',
     'phutil_unescape_uri_path_component' => 'markup/render.php',
     'phutil_units' => 'utils/utils.php',
     'phutil_utf8_console_strlen' => 'utils/utf8.php',
     'phutil_utf8_convert' => 'utils/utf8.php',
     'phutil_utf8_hard_wrap' => 'utils/utf8.php',
     'phutil_utf8_hard_wrap_html' => 'utils/utf8.php',
     'phutil_utf8_is_combining_character' => 'utils/utf8.php',
     'phutil_utf8_strlen' => 'utils/utf8.php',
     'phutil_utf8_strtolower' => 'utils/utf8.php',
     'phutil_utf8_strtoupper' => 'utils/utf8.php',
     'phutil_utf8_strtr' => 'utils/utf8.php',
     'phutil_utf8_ucwords' => 'utils/utf8.php',
     'phutil_utf8ize' => 'utils/utf8.php',
     'phutil_utf8v' => 'utils/utf8.php',
     'phutil_utf8v_codepoints' => 'utils/utf8.php',
     'phutil_utf8v_combined' => 'utils/utf8.php',
     'phutil_var_export' => 'utils/utils.php',
     'ppull' => 'utils/utils.php',
     'qsprintf' => 'xsprintf/qsprintf.php',
     'queryfx' => 'xsprintf/queryfx.php',
     'queryfx_all' => 'xsprintf/queryfx.php',
     'queryfx_one' => 'xsprintf/queryfx.php',
     'urisprintf' => 'xsprintf/urisprintf.php',
     'vcsprintf' => 'xsprintf/csprintf.php',
     'vjsprintf' => 'xsprintf/jsprintf.php',
     'vqsprintf' => 'xsprintf/qsprintf.php',
     'vqueryfx' => 'xsprintf/queryfx.php',
     'vqueryfx_all' => 'xsprintf/queryfx.php',
     'vurisprintf' => 'xsprintf/urisprintf.php',
     'xhp_parser_node_constants' => 'parser/xhpast/parser_nodes.php',
     'xhpast_parser_token_constants' => 'parser/xhpast/parser_tokens.php',
     'xsprintf' => 'xsprintf/xsprintf.php',
     'xsprintf_callback_example' => 'xsprintf/xsprintf.php',
     'xsprintf_command' => 'xsprintf/csprintf.php',
     'xsprintf_javascript' => 'xsprintf/jsprintf.php',
     'xsprintf_ldap' => 'xsprintf/ldapsprintf.php',
     'xsprintf_mercurial' => 'xsprintf/hgsprintf.php',
     'xsprintf_query' => 'xsprintf/qsprintf.php',
     'xsprintf_uri' => 'xsprintf/urisprintf.php',
   ),
   'xmap' => array(
     'AASTNodeList' => array(
       'Iterator',
       'Countable',
     ),
     'AbstractDirectedGraphTestCase' => 'PhutilTestCase',
     'AphrontAccessDeniedQueryException' => 'AphrontRecoverableQueryException',
     'AphrontBaseMySQLDatabaseConnection' => 'AphrontDatabaseConnection',
     'AphrontCharacterSetQueryException' => 'AphrontQueryException',
     'AphrontConnectionLostQueryException' => 'AphrontRecoverableQueryException',
     'AphrontConnectionQueryException' => 'AphrontQueryException',
     'AphrontCountQueryException' => 'AphrontQueryException',
     'AphrontDatabaseConnection' => 'PhutilQsprintfInterface',
     'AphrontDeadlockQueryException' => 'AphrontRecoverableQueryException',
     'AphrontDuplicateKeyQueryException' => 'AphrontQueryException',
     'AphrontIsolatedDatabaseConnection' => 'AphrontDatabaseConnection',
     'AphrontMySQLDatabaseConnection' => 'AphrontBaseMySQLDatabaseConnection',
     'AphrontMySQLiDatabaseConnection' => 'AphrontBaseMySQLDatabaseConnection',
     'AphrontNotSupportedQueryException' => 'AphrontQueryException',
     'AphrontObjectMissingQueryException' => 'AphrontQueryException',
     'AphrontParameterQueryException' => 'AphrontQueryException',
     'AphrontQueryException' => 'Exception',
     'AphrontRecoverableQueryException' => 'AphrontQueryException',
     'AphrontSchemaQueryException' => 'AphrontQueryException',
     'AphrontWriteGuardExitEventListener' => 'PhutilEventListener',
     'BaseHTTPFuture' => 'Future',
     'CommandException' => 'Exception',
     'ConduitClientException' => 'Exception',
     'ConduitClientTestCase' => 'PhutilTestCase',
     'ConduitFuture' => 'FutureProxy',
     'ExecFuture' => 'Future',
     'ExecFutureTestCase' => 'PhutilTestCase',
     'ExecPassthruTestCase' => 'PhutilTestCase',
     'FileFinderTestCase' => 'PhutilTestCase',
     'FilesystemException' => 'Exception',
     'FilesystemTestCase' => 'PhutilTestCase',
     'FutureIterator' => 'Iterator',
     'FutureIteratorTestCase' => 'PhutilTestCase',
     'FutureProxy' => 'Future',
     'HTTPFuture' => 'BaseHTTPFuture',
     'HTTPFutureCURLResponseStatus' => 'HTTPFutureResponseStatus',
     'HTTPFutureCertificateResponseStatus' => 'HTTPFutureResponseStatus',
     'HTTPFutureHTTPResponseStatus' => 'HTTPFutureResponseStatus',
     'HTTPFutureParseResponseStatus' => 'HTTPFutureResponseStatus',
     'HTTPFutureResponseStatus' => 'Exception',
     'HTTPFutureTransportResponseStatus' => 'HTTPFutureResponseStatus',
     'HTTPSFuture' => 'BaseHTTPFuture',
     'ImmediateFuture' => 'Future',
     'LinesOfALarge' => 'Iterator',
     'LinesOfALargeExecFuture' => 'LinesOfALarge',
     'LinesOfALargeExecFutureTestCase' => 'PhutilTestCase',
     'LinesOfALargeFile' => 'LinesOfALarge',
     'LinesOfALargeFileTestCase' => 'PhutilTestCase',
     'PHPASTParserTestCase' => 'PhutilTestCase',
     'PhageAgentTestCase' => 'PhutilTestCase',
     'PhagePHPAgentBootloader' => 'PhageAgentBootloader',
     'Phobject' => 'Iterator',
     'PhutilAPCKeyValueCache' => 'PhutilKeyValueCache',
     'PhutilAWSEC2Future' => 'PhutilAWSFuture',
     'PhutilAWSException' => 'Exception',
     'PhutilAWSFuture' => 'FutureProxy',
     'PhutilAWSS3Future' => 'PhutilAWSFuture',
     'PhutilAggregateException' => 'Exception',
     'PhutilAllCapsEnglishLocale' => 'PhutilLocale',
     'PhutilAmazonAuthAdapter' => 'PhutilOAuthAuthAdapter',
     'PhutilArgumentParserException' => 'Exception',
     'PhutilArgumentParserTestCase' => 'PhutilTestCase',
     'PhutilArgumentSpecificationException' => 'PhutilArgumentParserException',
     'PhutilArgumentSpecificationTestCase' => 'PhutilTestCase',
     'PhutilArgumentUsageException' => 'PhutilArgumentParserException',
     'PhutilArray' => array(
       'Phobject',
       'Countable',
       'ArrayAccess',
       'Iterator',
     ),
     'PhutilArrayTestCase' => 'PhutilTestCase',
     'PhutilArrayWithDefaultValue' => 'PhutilArray',
     'PhutilAsanaAuthAdapter' => 'PhutilOAuthAuthAdapter',
     'PhutilAsanaFuture' => 'FutureProxy',
     'PhutilAuthConfigurationException' => 'PhutilAuthException',
     'PhutilAuthCredentialException' => 'PhutilAuthException',
     'PhutilAuthException' => 'Exception',
     'PhutilAuthUserAbortedException' => 'PhutilAuthException',
     'PhutilBitbucketAuthAdapter' => 'PhutilOAuth1AuthAdapter',
     'PhutilBootloaderException' => 'Exception',
     'PhutilBritishEnglishLocale' => 'PhutilLocale',
     'PhutilBufferedIterator' => 'Iterator',
     'PhutilBufferedIteratorTestCase' => 'PhutilTestCase',
     'PhutilBugtraqParserTestCase' => 'PhutilTestCase',
     'PhutilCIDRBlock' => 'Phobject',
     'PhutilCIDRList' => 'Phobject',
     'PhutilCLikeCodeSnippetContextFreeGrammar' => 'PhutilCodeSnippetContextFreeGrammar',
     'PhutilCallbackFilterIterator' => 'FilterIterator',
     'PhutilChannelChannel' => 'PhutilChannel',
     'PhutilChannelTestCase' => 'PhutilTestCase',
     'PhutilChunkedIterator' => 'Iterator',
     'PhutilChunkedIteratorTestCase' => 'PhutilTestCase',
     'PhutilCodeSnippetContextFreeGrammar' => 'PhutilContextFreeGrammar',
     'PhutilCommandString' => 'Phobject',
     'PhutilConsoleProgressBar' => 'Phobject',
     'PhutilConsoleServerChannel' => 'PhutilChannelChannel',
     'PhutilConsoleStdinNotInteractiveException' => 'Exception',
     'PhutilConsoleTable' => 'Phobject',
     'PhutilConsoleWrapTestCase' => 'PhutilTestCase',
     'PhutilCsprintfTestCase' => 'PhutilTestCase',
     'PhutilCzechLocale' => 'PhutilLocale',
     'PhutilDefaultSyntaxHighlighterEngine' => 'PhutilSyntaxHighlighterEngine',
     'PhutilDefaultSyntaxHighlighterEnginePygmentsFuture' => 'FutureProxy',
     'PhutilDefaultSyntaxHighlighterEngineTestCase' => 'PhutilTestCase',
     'PhutilDeferredLogTestCase' => 'PhutilTestCase',
     'PhutilDirectedScalarGraph' => 'AbstractDirectedGraph',
     'PhutilDirectoryKeyValueCache' => 'PhutilKeyValueCache',
     'PhutilDisqusAuthAdapter' => 'PhutilOAuthAuthAdapter',
     'PhutilDocblockParserTestCase' => 'PhutilTestCase',
     'PhutilEditDistanceMatrixTestCase' => 'PhutilTestCase',
     'PhutilEmailAddressTestCase' => 'PhutilTestCase',
     'PhutilEmptyAuthAdapter' => 'PhutilAuthAdapter',
     'PhutilErrorHandlerTestCase' => 'PhutilTestCase',
     'PhutilErrorTrap' => 'Phobject',
     'PhutilEventType' => 'PhutilEventConstants',
     'PhutilExampleBufferedIterator' => 'PhutilBufferedIterator',
     'PhutilExcessiveServiceCallsDaemon' => 'PhutilTortureTestDaemon',
     'PhutilExecChannel' => 'PhutilChannel',
     'PhutilExecPassthru' => 'Phobject',
     'PhutilExtensionsTestCase' => 'PhutilTestCase',
     'PhutilFacebookAuthAdapter' => 'PhutilOAuthAuthAdapter',
     'PhutilFatalDaemon' => 'PhutilTortureTestDaemon',
     'PhutilFileLock' => 'PhutilLock',
     'PhutilFileLockTestCase' => 'PhutilTestCase',
     'PhutilGitHubAuthAdapter' => 'PhutilOAuthAuthAdapter',
     'PhutilGitURITestCase' => 'PhutilTestCase',
     'PhutilGoogleAuthAdapter' => 'PhutilOAuthAuthAdapter',
     'PhutilHangForeverDaemon' => 'PhutilTortureTestDaemon',
     'PhutilHelpArgumentWorkflow' => 'PhutilArgumentWorkflow',
     'PhutilHgsprintfTestCase' => 'PhutilTestCase',
+    'PhutilHighIntensityIntervalDaemon' => 'PhutilTortureTestDaemon',
     'PhutilINIParserException' => 'Exception',
     'PhutilIPAddress' => 'Phobject',
     'PhutilIPAddressTestCase' => 'PhutilTestCase',
     'PhutilInRequestKeyValueCache' => 'PhutilKeyValueCache',
     'PhutilInvalidRuleParserGeneratorException' => 'PhutilParserGeneratorException',
     'PhutilIrreducibleRuleParserGeneratorException' => 'PhutilParserGeneratorException',
     'PhutilJIRAAuthAdapter' => 'PhutilOAuth1AuthAdapter',
     'PhutilJSONParserException' => 'Exception',
     'PhutilJSONParserTestCase' => 'PhutilTestCase',
     'PhutilJSONProtocolChannel' => 'PhutilProtocolChannel',
     'PhutilJSONProtocolChannelTestCase' => 'PhutilTestCase',
     'PhutilJSONTestCase' => 'PhutilTestCase',
     'PhutilJavaCodeSnippetContextFreeGrammar' => 'PhutilCLikeCodeSnippetContextFreeGrammar',
     'PhutilKeyValueCacheNamespace' => 'PhutilKeyValueCacheProxy',
     'PhutilKeyValueCacheProfiler' => 'PhutilKeyValueCacheProxy',
     'PhutilKeyValueCacheProxy' => 'PhutilKeyValueCache',
     'PhutilKeyValueCacheStack' => 'PhutilKeyValueCache',
     'PhutilKeyValueCacheTestCase' => 'PhutilTestCase',
     'PhutilLDAPAuthAdapter' => 'PhutilAuthAdapter',
     'PhutilLanguageGuesserTestCase' => 'PhutilTestCase',
     'PhutilLexerSyntaxHighlighter' => 'PhutilSyntaxHighlighter',
     'PhutilLibraryConflictException' => 'Exception',
     'PhutilLibraryTestCase' => 'PhutilTestCase',
     'PhutilLipsumContextFreeGrammar' => 'PhutilContextFreeGrammar',
     'PhutilLocale' => 'Phobject',
     'PhutilLockException' => 'Exception',
     'PhutilLogFileChannel' => 'PhutilChannelChannel',
     'PhutilLunarPhaseTestCase' => 'PhutilTestCase',
     'PhutilMarkupTestCase' => 'PhutilTestCase',
     'PhutilMemcacheKeyValueCache' => 'PhutilKeyValueCache',
     'PhutilMethodNotImplementedException' => 'Exception',
     'PhutilMetricsChannel' => 'PhutilChannelChannel',
     'PhutilMissingSymbolException' => 'Exception',
     'PhutilModuleUtilsTestCase' => 'PhutilTestCase',
     'PhutilNiceDaemon' => 'PhutilTortureTestDaemon',
     'PhutilOAuth1AuthAdapter' => 'PhutilAuthAdapter',
     'PhutilOAuth1Future' => 'FutureProxy',
     'PhutilOAuth1FutureTestCase' => 'PhutilTestCase',
     'PhutilOAuthAuthAdapter' => 'PhutilAuthAdapter',
     'PhutilOnDiskKeyValueCache' => 'PhutilKeyValueCache',
     'PhutilOpaqueEnvelopeTestCase' => 'PhutilTestCase',
     'PhutilPHPCodeSnippetContextFreeGrammar' => 'PhutilCLikeCodeSnippetContextFreeGrammar',
     'PhutilPHPFragmentLexer' => 'PhutilLexer',
     'PhutilPHPFragmentLexerHighlighterTestCase' => 'PhutilTestCase',
     'PhutilPHPFragmentLexerTestCase' => 'PhutilTestCase',
     'PhutilPHPObjectProtocolChannel' => 'PhutilProtocolChannel',
     'PhutilPHPObjectProtocolChannelTestCase' => 'PhutilTestCase',
     'PhutilParserGeneratorException' => 'Exception',
     'PhutilParserGeneratorTestCase' => 'PhutilTestCase',
     'PhutilPayPalAPIFuture' => 'FutureProxy',
     'PhutilPersonTest' => 'PhutilPerson',
     'PhutilPersonaAuthAdapter' => 'PhutilAuthAdapter',
     'PhutilPhabricatorAuthAdapter' => 'PhutilOAuthAuthAdapter',
     'PhutilPhobjectTestCase' => 'PhutilTestCase',
     'PhutilPhtTestCase' => 'PhutilTestCase',
     'PhutilProcessGroupDaemon' => 'PhutilTortureTestDaemon',
     'PhutilProtocolChannel' => 'PhutilChannelChannel',
     'PhutilProxyException' => 'Exception',
     'PhutilPythonFragmentLexer' => 'PhutilLexer',
     'PhutilQueryStringParserTestCase' => 'PhutilTestCase',
     'PhutilRawEnglishLocale' => 'PhutilLocale',
     'PhutilReadableSerializerTestCase' => 'PhutilTestCase',
     'PhutilRealNameContextFreeGrammar' => 'PhutilContextFreeGrammar',
     'PhutilRemarkupBoldRule' => 'PhutilRemarkupRule',
     'PhutilRemarkupCodeBlockRule' => 'PhutilRemarkupBlockRule',
     'PhutilRemarkupDefaultBlockRule' => 'PhutilRemarkupBlockRule',
     'PhutilRemarkupDelRule' => 'PhutilRemarkupRule',
     'PhutilRemarkupDocumentLinkRule' => 'PhutilRemarkupRule',
     'PhutilRemarkupEngine' => 'PhutilMarkupEngine',
     'PhutilRemarkupEngineTestCase' => 'PhutilTestCase',
     'PhutilRemarkupEscapeRemarkupRule' => 'PhutilRemarkupRule',
     'PhutilRemarkupHeaderBlockRule' => 'PhutilRemarkupBlockRule',
     'PhutilRemarkupHorizontalRuleBlockRule' => 'PhutilRemarkupBlockRule',
     'PhutilRemarkupHyperlinkRule' => 'PhutilRemarkupRule',
     'PhutilRemarkupInlineBlockRule' => 'PhutilRemarkupBlockRule',
     'PhutilRemarkupInterpreterBlockRule' => 'PhutilRemarkupBlockRule',
     'PhutilRemarkupItalicRule' => 'PhutilRemarkupRule',
     'PhutilRemarkupLinebreaksRule' => 'PhutilRemarkupRule',
     'PhutilRemarkupListBlockRule' => 'PhutilRemarkupBlockRule',
     'PhutilRemarkupLiteralBlockRule' => 'PhutilRemarkupBlockRule',
     'PhutilRemarkupMonospaceRule' => 'PhutilRemarkupRule',
     'PhutilRemarkupNoteBlockRule' => 'PhutilRemarkupBlockRule',
     'PhutilRemarkupQuotesBlockRule' => 'PhutilRemarkupBlockRule',
     'PhutilRemarkupReplyBlockRule' => 'PhutilRemarkupBlockRule',
     'PhutilRemarkupSimpleTableBlockRule' => 'PhutilRemarkupBlockRule',
     'PhutilRemarkupTableBlockRule' => 'PhutilRemarkupBlockRule',
     'PhutilRemarkupTestInterpreterRule' => 'PhutilRemarkupBlockInterpreter',
     'PhutilRemarkupUnderlineRule' => 'PhutilRemarkupRule',
     'PhutilRope' => 'Phobject',
     'PhutilRopeTestCase' => 'PhutilTestCase',
     'PhutilSafeHTMLTestCase' => 'PhutilTestCase',
     'PhutilSaturateStdoutDaemon' => 'PhutilTortureTestDaemon',
     'PhutilShellLexer' => 'PhutilLexer',
     'PhutilShellLexerTestCase' => 'PhutilTestCase',
     'PhutilSimpleOptionsLexer' => 'PhutilLexer',
     'PhutilSimpleOptionsLexerTestCase' => 'PhutilTestCase',
     'PhutilSimpleOptionsTestCase' => 'PhutilTestCase',
     'PhutilSocketChannel' => 'PhutilChannel',
     'PhutilSyntaxHighlighterException' => 'Exception',
     'PhutilSystem' => 'Phobject',
     'PhutilSystemTestCase' => 'PhutilTestCase',
     'PhutilTestCase' => 'ArcanistPhutilTestCase',
     'PhutilTestPhobject' => 'Phobject',
     'PhutilTortureTestDaemon' => 'PhutilDaemon',
     'PhutilTranslation' => 'Phobject',
     'PhutilTranslatorTestCase' => 'PhutilTestCase',
     'PhutilTwitchAuthAdapter' => 'PhutilOAuthAuthAdapter',
     'PhutilTwitchFuture' => 'FutureProxy',
     'PhutilTwitterAuthAdapter' => 'PhutilOAuth1AuthAdapter',
     'PhutilTypeCheckException' => 'Exception',
     'PhutilTypeExtraParametersException' => 'Exception',
     'PhutilTypeLexer' => 'PhutilLexer',
     'PhutilTypeMissingParametersException' => 'Exception',
     'PhutilTypeSpecTestCase' => 'PhutilTestCase',
     'PhutilURITestCase' => 'PhutilTestCase',
     'PhutilUSEnglishLocale' => 'PhutilLocale',
     'PhutilUTF8StringTruncator' => 'Phobject',
     'PhutilUTF8TestCase' => 'PhutilTestCase',
     'PhutilUnknownSymbolParserGeneratorException' => 'PhutilParserGeneratorException',
     'PhutilUnreachableRuleParserGeneratorException' => 'PhutilParserGeneratorException',
     'PhutilUnreachableTerminalParserGeneratorException' => 'PhutilParserGeneratorException',
     'PhutilUrisprintfTestCase' => 'PhutilTestCase',
     'PhutilUtilsTestCase' => 'PhutilTestCase',
     'PhutilVeryWowEnglishLocale' => 'PhutilLocale',
     'PhutilWordPressAuthAdapter' => 'PhutilOAuthAuthAdapter',
     'PhutilWordPressFuture' => 'FutureProxy',
     'PhutilXHPASTSyntaxHighlighterFuture' => 'FutureProxy',
     'PhutilXHPASTSyntaxHighlighterTestCase' => 'PhutilTestCase',
     'QueryFuture' => 'Future',
     'TestAbstractDirectedGraph' => 'AbstractDirectedGraph',
     'XHPASTNode' => 'AASTNode',
     'XHPASTNodeTestCase' => 'PhutilTestCase',
     'XHPASTSyntaxErrorException' => 'Exception',
     'XHPASTToken' => 'AASTToken',
     'XHPASTTree' => 'AASTTree',
     'XHPASTTreeTestCase' => 'PhutilTestCase',
     'XsprintfUnknownConversionException' => 'InvalidArgumentException',
   ),
 ));
diff --git a/src/daemon/PhutilDaemon.php b/src/daemon/PhutilDaemon.php
index bec8ab6..c39c433 100644
--- a/src/daemon/PhutilDaemon.php
+++ b/src/daemon/PhutilDaemon.php
@@ -1,208 +1,391 @@
 <?php
 
 /**
  * Scaffolding for implementing robust background processing scripts.
  *
  *
+ * Autoscaling
+ * ===========
+ *
+ * Autoscaling automatically launches copies of a daemon when it is busy
+ * (scaling the pool up) and stops them when they're idle (scaling the pool
+ * down). This is appropriate for daemons which perform highly parallelizable
+ * work.
+ *
+ * To make a daemon support autoscaling, the implementation should look
+ * something like this:
+ *
+ *   while (!$this->shouldExit()) {
+ *     if (work_available()) {
+ *       $this->willBeginWork();
+ *       do_work();
+ *       $this->sleep(0);
+ *     } else {
+ *       $this->willBeginIdle();
+ *       $this->sleep(1);
+ *     }
+ *   }
+ *
+ * In particular, call @{method:willBeginWork} before becoming busy, and
+ * @{method:willBeginIdle} when no work is available. If the daemon is launched
+ * into an autoscale pool, this will cause the pool to automatically scale up
+ * when busy and down when idle.
+ *
+ * See @{class:PhutilHighIntensityIntervalDaemon} for an example of a simple
+ * autoscaling daemon.
+ *
+ * Launching a daemon which does not make these callbacks into an autoscale
+ * pool will have no effect.
+ *
  * @task overseer Communicating With the Overseer
+ * @task autoscale Autoscaling Daemon Pools
  *
  * @stable
  */
 abstract class PhutilDaemon {
 
   const MESSAGETYPE_STDOUT = 'stdout';
   const MESSAGETYPE_HEARTBEAT = 'heartbeat';
+  const MESSAGETYPE_BUSY = 'busy';
+  const MESSAGETYPE_IDLE = 'idle';
+  const MESSAGETYPE_DOWN = 'down';
+
+  const WORKSTATE_BUSY = 'busy';
+  const WORKSTATE_IDLE = 'idle';
 
   private $argv;
   private $traceMode;
   private $traceMemory;
   private $verbose;
   private $notifyReceived;
   private $inGracefulShutdown;
+  private $workState = null;
+  private $idleSince = null;
+  private $autoscaleProperties = array();
 
   final public function setVerbose($verbose) {
     $this->verbose = $verbose;
     return $this;
   }
 
   final public function getVerbose() {
     return $this->verbose;
   }
 
   private static $sighandlerInstalled;
 
   final public function __construct(array $argv) {
     declare(ticks = 1);
     $this->argv = $argv;
 
     if (!self::$sighandlerInstalled) {
       self::$sighandlerInstalled = true;
       pcntl_signal(SIGTERM, __CLASS__.'::exitOnSignal');
     }
 
     pcntl_signal(SIGINT,  array($this, 'onGracefulSignal'));
     pcntl_signal(SIGUSR2, array($this, 'onNotifySignal'));
 
     // Without discard mode, this consumes unbounded amounts of memory. Keep
     // memory bounded.
     PhutilServiceProfiler::getInstance()->enableDiscardMode();
 
     $this->beginStdoutCapture();
   }
 
   final public function __destruct() {
     $this->endStdoutCapture();
   }
 
   final public function stillWorking() {
     $this->emitOverseerMessage(self::MESSAGETYPE_HEARTBEAT, null);
 
     if ($this->traceMemory) {
       $memuse = number_format(memory_get_usage() / 1024, 1);
       $daemon = get_class($this);
       fprintf(STDERR, '%s', "<RAMS> {$daemon} Memory Usage: {$memuse} KB\n");
     }
   }
 
   final public function shouldExit() {
     return $this->inGracefulShutdown;
   }
 
   final protected function sleep($duration) {
     $this->notifyReceived = false;
     $this->willSleep($duration);
     $this->stillWorking();
+
+    $is_autoscale = $this->isClonedAutoscaleDaemon();
+    $scale_down = $this->getAutoscaleDownDuration();
+
+    $max_sleep = 60;
+    if ($is_autoscale) {
+      $max_sleep = min($max_sleep, $scale_down);
+    }
+
+    if ($is_autoscale) {
+      if ($this->workState == self::WORKSTATE_IDLE) {
+        $dur = (time() - $this->idleSince);
+        $this->log(pht('Idle for %s seconds.', $dur));
+      }
+    }
+
     while ($duration > 0 &&
       !$this->notifyReceived &&
       !$this->shouldExit()) {
-      sleep(min($duration, 60));
-      $duration -= 60;
+
+      // If this is an autoscaling clone and we've been idle for too long,
+      // we're going to scale the pool down by exiting and not restarting. The
+      // DOWN message tells the overseer that we don't want to be restarted.
+      if ($is_autoscale) {
+        if ($this->workState == self::WORKSTATE_IDLE) {
+          if ($this->idleSince && ($this->idleSince + $scale_down < time())) {
+            $this->inGracefulShutdown = true;
+            $this->emitOverseerMessage(self::MESSAGETYPE_DOWN, null);
+            $this->log(
+              pht(
+                'Daemon was idle for more than %s seconds, scaling pool '.
+                'down.',
+                $scale_down));
+            break;
+          }
+        }
+      }
+
+      sleep(min($duration, $max_sleep));
+      $duration -= $max_sleep;
       $this->stillWorking();
     }
   }
 
   protected function willSleep($duration) {
     return;
   }
 
   public static function exitOnSignal($signo) {
     // Normally, PHP doesn't invoke destructors when existing in response to
     // a signal. This forces it to do so, so we have a fighting chance of
     // releasing any locks, leases or resources on our way out.
     exit(128 + $signo);
   }
 
   final protected function getArgv() {
     return $this->argv;
   }
 
   final public function execute() {
     $this->willRun();
     $this->run();
   }
 
   abstract protected function run();
 
   final public function setTraceMemory() {
     $this->traceMemory = true;
     return $this;
   }
 
   final public function getTraceMemory() {
     return $this->traceMemory;
   }
 
   final public function setTraceMode() {
     $this->traceMode = true;
     PhutilServiceProfiler::installEchoListener();
     PhutilConsole::getConsole()->getServer()->setEnableLog(true);
     $this->didSetTraceMode();
     return $this;
   }
 
   final public function getTraceMode() {
     return $this->traceMode;
   }
 
   final public function onGracefulSignal($signo) {
     $this->inGracefulShutdown = true;
   }
 
   final public function onNotifySignal($signo) {
     $this->notifyReceived = true;
     $this->onNotify($signo);
   }
 
   protected function onNotify($signo) {
     // This is a hook for subclasses.
   }
 
   protected function willRun() {
     // This is a hook for subclasses.
   }
 
   protected function didSetTraceMode() {
     // This is a hook for subclasses.
   }
 
   final protected function log($message) {
     if ($this->verbose) {
       $daemon = get_class($this);
       fprintf(STDERR, '%s', "<VERB> {$daemon} {$message}\n");
     }
   }
 
 
 /* -(  Communicating With the Overseer  )------------------------------------ */
 
 
   private function beginStdoutCapture() {
     ob_start(array($this, 'didReceiveStdout'), 2);
   }
 
   private function endStdoutCapture() {
     ob_end_flush();
   }
 
   public function didReceiveStdout($data) {
     if (!strlen($data)) {
       return '';
     }
     return $this->encodeOverseerMessage(self::MESSAGETYPE_STDOUT, $data);
   }
 
   private function encodeOverseerMessage($type, $data) {
     $structure = array($type);
 
     if ($data !== null) {
       $structure[] = $data;
     }
 
     return json_encode($structure)."\n";
   }
 
   private function emitOverseerMessage($type, $data) {
     $this->endStdoutCapture();
     echo $this->encodeOverseerMessage($type, $data);
     $this->beginStdoutCapture();
   }
 
   public static function errorListener($event, $value, array $metadata) {
     // If the caller has redirected the error log to a file, PHP won't output
     // messages to stderr, so the overseer can't capture them. Install a
     // listener which just  echoes errors to stderr, so the overseer is always
     // aware of errors.
 
     $console = PhutilConsole::getConsole();
     $message = idx($metadata, 'default_message');
 
     if ($message) {
       $console->writeErr("%s\n", $message);
     }
     if (idx($metadata, 'trace')) {
       $trace = PhutilErrorHandler::formatStacktrace($metadata['trace']);
       $console->writeErr("%s\n", $trace);
     }
   }
 
+
+/* -(  Autoscaling  )-------------------------------------------------------- */
+
+
+  /**
+   * Prepare to become busy. This may autoscale the pool up.
+   *
+   * This notifies the overseer that the daemon has become busy. If daemons
+   * that are part of an autoscale pool are continuously busy for a prolonged
+   * period of time, the overseer may scale up the pool.
+   *
+   * @return this
+   * @task autoscale
+   */
+  protected function willBeginWork() {
+    if ($this->workState != self::WORKSTATE_BUSY) {
+      $this->workState = self::WORKSTATE_BUSY;
+      $this->idleSince = null;
+      $this->emitOverseerMessage(self::MESSAGETYPE_BUSY, null);
+    }
+
+    return $this;
+  }
+
+
+  /**
+   * Prepare to idle. This may autoscale the pool down.
+   *
+   * This notifies the overseer that the daemon is no longer busy. If daemons
+   * that are part of an autoscale pool are idle for a prolonged period of time,
+   * they may exit to scale the pool down.
+   *
+   * @return this
+   * @task autoscale
+   */
+  protected function willBeginIdle() {
+    if ($this->workState != self::WORKSTATE_IDLE) {
+      $this->workState = self::WORKSTATE_IDLE;
+      $this->idleSince = time();
+      $this->emitOverseerMessage(self::MESSAGETYPE_IDLE, null);
+    }
+
+    return $this;
+  }
+
+
+  /**
+   * Determine if this is a clone or the original daemon.
+   *
+   * @return bool True if this is an cloned autoscaling daemon.
+   * @task autoscale
+   */
+  private function isClonedAutoscaleDaemon() {
+    return (bool)$this->getAutoscaleProperty('clone', false);
+  }
+
+
+  /**
+   * Get the duration (in seconds) which a daemon must be continuously idle
+   * for before it should exit to scale the pool down.
+   *
+   * @return int Duration, in seconds.
+   * @task autoscale
+   */
+  private function getAutoscaleDownDuration() {
+    return $this->getAutoscaleProperty('down', 15);
+  }
+
+
+  /**
+   * Configure autoscaling for this daemon.
+   *
+   * @param map<string, wild> Map of autoscale properties.
+   * @return this
+   * @task autoscale
+   */
+  public function setAutoscaleProperties(array $autoscale_properties) {
+    PhutilTypeSpec::checkMap(
+      $autoscale_properties,
+      array(
+        'group' => 'optional string',
+        'up' => 'optional int',
+        'down' => 'optional int',
+        'pool' => 'optional int',
+        'clone' => 'optional bool',
+      ));
+
+    $this->autoscaleProperties = $autoscale_properties;
+
+    return $this;
+  }
+
+
+  /**
+   * Read autoscaling configuration for this daemon.
+   *
+   * @param string Property to read.
+   * @param wild Default value to return if the property is not set.
+   * @return wild Property value, or `$default` if one is not set.
+   * @task autoscale
+   */
+  private function getAutoscaleProperty($key, $default = null) {
+    return idx($this->autoscaleProperties, $key, $default);
+  }
+
 }
diff --git a/src/daemon/PhutilDaemonHandle.php b/src/daemon/PhutilDaemonHandle.php
index d11973a..82a5cea 100644
--- a/src/daemon/PhutilDaemonHandle.php
+++ b/src/daemon/PhutilDaemonHandle.php
@@ -1,379 +1,399 @@
 <?php
 
 final class PhutilDaemonHandle {
 
   const EVENT_DID_LAUNCH    = 'daemon.didLaunch';
   const EVENT_DID_LOG       = 'daemon.didLogMessage';
   const EVENT_DID_HEARTBEAT = 'daemon.didHeartbeat';
   const EVENT_WILL_GRACEFUL = 'daemon.willGraceful';
   const EVENT_WILL_EXIT     = 'daemon.willExit';
 
   private $overseer;
   private $daemonClass;
   private $argv;
   private $pid;
   private $daemonID;
   private $deadline;
   private $heartbeat;
   private $stdoutBuffer;
   private $restartAt;
   private $silent;
+  private $shouldRestart = true;
   private $shouldShutdown;
   private $future;
   private $traceMemory;
 
   public function __construct(
     PhutilDaemonOverseer $overseer,
     $daemon_class,
     array $argv,
     array $config) {
 
     $this->overseer = $overseer;
     $this->daemonClass = $daemon_class;
     $this->argv = $argv;
     $this->config = $config;
     $this->restartAt = time();
 
     $this->daemonID = $this->generateDaemonID();
     $this->dispatchEvent(
       self::EVENT_DID_LAUNCH,
       array(
         'argv' => $this->argv,
         'explicitArgv' => idx($this->config, 'argv'),
       ));
   }
 
   public function isRunning() {
     return (bool)$this->future;
   }
 
+  public function isDone() {
+    return (!$this->shouldRestart && !$this->isRunning());
+  }
+
   public function getFuture() {
     return $this->future;
   }
 
   public function setSilent($silent) {
     $this->silent = $silent;
     return $this;
   }
 
   public function getSilent() {
     return $this->silent;
   }
 
   public function setTraceMemory($trace_memory) {
     $this->traceMemory = $trace_memory;
     return $this;
   }
 
   public function getTraceMemory() {
     return $this->traceMemory;
   }
 
   public function update() {
     $this->updateMemory();
 
     if (!$this->isRunning()) {
+      if (!$this->shouldRestart) {
+        return;
+      }
       if (!$this->restartAt || (time() < $this->restartAt)) {
         return;
       }
       if ($this->shouldShutdown) {
         return;
       }
       $this->startDaemonProcess();
     }
 
     $future = $this->future;
 
     $result = null;
     if ($future->isReady()) {
       $result = $future->resolve();
     }
 
     list($stdout, $stderr) = $future->read();
     $future->discardBuffers();
 
     if (strlen($stdout)) {
       $this->didReadStdout($stdout);
     }
 
     $stderr = trim($stderr);
     if (strlen($stderr)) {
       $this->logMessage('STDE', $stderr);
     }
 
     if ($result !== null) {
       list($err) = $result;
       if ($err) {
         $this->logMessage('FAIL', pht('Process exited with error %s', $err));
       } else {
         $this->logMessage('DONE', pht('Process exited normally.'));
       }
 
       $this->future = null;
 
       if ($this->shouldShutdown) {
         $this->restartAt = null;
         $this->dispatchEvent(self::EVENT_WILL_EXIT);
       } else {
         $this->scheduleRestart();
       }
     }
 
     $this->updateHeartbeatEvent();
     $this->updateHangDetection();
   }
 
   private function updateHeartbeatEvent() {
     if ($this->heartbeat > time()) {
       return;
     }
 
     $this->heartbeat = time() + $this->getHeartbeatEventFrequency();
     $this->dispatchEvent(self::EVENT_DID_HEARTBEAT);
   }
 
   private function updateHangDetection() {
     if (!$this->isRunning()) {
       return;
     }
 
     if (time() > $this->deadline) {
       $this->logMessage('HANG', pht('Hang detected. Restarting process.'));
       $this->annihilateProcessGroup();
       $this->scheduleRestart();
     }
   }
 
   private function scheduleRestart() {
     $this->logMessage('WAIT', pht('Waiting to restart process.'));
     $this->restartAt = time() + self::getWaitBeforeRestart();
   }
 
   /**
    * Generate a unique ID for this daemon.
    *
    * @return string A unique daemon ID.
    */
   private function generateDaemonID() {
     return substr(getmypid().':'.Filesystem::readRandomCharacters(12), 0, 12);
   }
 
   public function getDaemonID() {
     return $this->daemonID;
   }
 
   public function getPID() {
     return $this->pid;
   }
 
   private function getCaptureBufferSize() {
     return 65535;
   }
 
   private function getRequiredHeartbeatFrequency() {
     return 86400;
   }
 
   public static function getWaitBeforeRestart() {
     return 5;
   }
 
   public static function getHeartbeatEventFrequency() {
     return 120;
   }
 
   private function getKillDelay() {
     return 3;
   }
 
   private function getDaemonCWD() {
     $root = dirname(phutil_get_library_root('phutil'));
     return $root.'/scripts/daemon/exec/';
   }
 
   private function newExecFuture() {
     $class = $this->daemonClass;
     $argv = $this->argv;
     $buffer_size = $this->getCaptureBufferSize();
 
     // NOTE: PHP implements proc_open() by running 'sh -c'. On most systems this
     // is bash, but on Ubuntu it's dash. When you proc_open() using bash, you
     // get one new process (the command you ran). When you proc_open() using
     // dash, you get two new processes: the command you ran and a parent
     // "dash -c" (or "sh -c") process. This means that the child process's PID
     // is actually the 'dash' PID, not the command's PID. To avoid this, use
     // 'exec' to replace the shell process with the real process; without this,
     // the child will call posix_getppid(), be given the pid of the 'sh -c'
     // process, and send it SIGUSR1 to keepalive which will terminate it
     // immediately. We also won't be able to do process group management because
     // the shell process won't properly posix_setsid() so the pgid of the child
     // won't be meaningful.
 
     return id(new ExecFuture('exec ./exec_daemon.php %s %Ls', $class, $argv))
       ->setCWD($this->getDaemonCWD())
       ->setStdoutSizeLimit($buffer_size)
       ->setStderrSizeLimit($buffer_size)
       ->write(json_encode($this->config));
   }
 
   /**
    * Dispatch an event to event listeners.
    *
    * @param  string Event type.
    * @param  dict   Event parameters.
    * @return void
    */
   private function dispatchEvent($type, array $params = array()) {
     $data = array(
       'id' => $this->daemonID,
       'daemonClass' => $this->daemonClass,
       'childPID' => $this->pid,
     ) + $params;
 
     $event = new PhutilEvent($type, $data);
 
     try {
       PhutilEventEngine::dispatchEvent($event);
     } catch (Exception $ex) {
       phlog($ex);
     }
   }
 
   private function annihilateProcessGroup() {
     $pid = $this->pid;
     $pgid = posix_getpgid($pid);
     if ($pid && $pgid) {
 
       // NOTE: On Ubuntu, 'kill' does not recognize the use of "--" to
       // explicitly delineate PID/PGIDs from signals. We don't actually need it,
       // so use the implicit "kill -TERM -pgid" form instead of the explicit
       // "kill -TERM -- -pgid" form.
       exec("kill -TERM -{$pgid}");
       sleep($this->getKillDelay());
 
       // On OSX, we'll get a permission error on stderr if the SIGTERM was
       // successful in ending the life of the process group, presumably because
       // all that's left is the daemon itself as a zombie waiting for us to
       // reap it. However, we still need to issue this command for process
       // groups that resist SIGTERM. Rather than trying to figure out if the
       // process group is still around or not, just SIGKILL unconditionally and
       // ignore any error which may be raised.
       exec("kill -KILL -{$pgid} 2>/dev/null");
       $this->pid = null;
     }
   }
 
 
   private function gracefulProcessGroup() {
     $pid = $this->pid;
     $pgid = posix_getpgid($pid);
     if ($pid && $pgid) {
       exec("kill -INT -{$pgid}");
     }
   }
 
   private function updateMemory() {
     if ($this->traceMemory) {
       $memuse = number_format(memory_get_usage() / 1024, 1);
       $this->logMessage('RAMS', 'Overseer Memory Usage: '.$memuse.' KB');
     }
   }
 
   private function startDaemonProcess() {
     $this->logMessage('INIT', pht('Starting process.'));
 
     $this->deadline = time() + $this->getRequiredHeartbeatFrequency();
     $this->heartbeat = time() + self::getHeartbeatEventFrequency();
     $this->stdoutBuffer = '';
 
     $this->future = $this->newExecFuture();
     $this->future->start();
 
     $this->pid = $this->future->getPID();
   }
 
   private function didReadStdout($data) {
     $this->stdoutBuffer .= $data;
     while (true) {
       $pos = strpos($this->stdoutBuffer, "\n");
       if ($pos === false) {
         break;
       }
       $message = substr($this->stdoutBuffer, 0, $pos);
       $this->stdoutBuffer = substr($this->stdoutBuffer, $pos + 1);
 
       $structure = @json_decode($message, true);
       if (!is_array($structure)) {
         $structure = array();
       }
 
       switch (idx($structure, 0)) {
         case PhutilDaemon::MESSAGETYPE_STDOUT:
           $this->logMessage('STDO', idx($structure, 1));
           break;
         case PhutilDaemon::MESSAGETYPE_HEARTBEAT:
           $this->deadline = time() + $this->getRequiredHeartbeatFrequency();
           break;
+        case PhutilDaemon::MESSAGETYPE_BUSY:
+          $this->overseer->didBeginWork($this);
+          break;
+        case PhutilDaemon::MESSAGETYPE_IDLE:
+          $this->overseer->didBeginIdle($this);
+          break;
+        case PhutilDaemon::MESSAGETYPE_DOWN:
+          // The daemon is exiting because it doesn't have enough work and it
+          // is trying to scale the pool down. We should not restart it.
+          $this->shouldRestart = false;
+          $this->shouldShutdown = true;
+          break;
         default:
           // If we can't parse this or it isn't a message we understand, just
           // emit the raw message.
           $this->logMessage('STDO', pht('<Malformed> %s', $message));
           break;
       }
     }
   }
 
   public function didReceiveNotifySignal($signo) {
     $pid = $this->pid;
     if ($pid) {
       posix_kill($pid, $signo);
     }
   }
 
   public function didReceiveGracefulSignal($signo) {
     $this->shouldShutdown = true;
 
     $signame = phutil_get_signal_name($signo);
     if ($signame) {
       $sigmsg = pht(
         'Graceful shutdown in response to signal %d (%s).',
         $signo,
         $signame);
     } else {
       $sigmsg = pht(
         'Graceful shutdown in response to signal %d.',
         $signo);
     }
 
     $this->logMessage('DONE', $sigmsg, $signo);
     $this->gracefulProcessGroup();
   }
 
   public function didReceiveTerminalSignal($signo) {
     $signame = phutil_get_signal_name($signo);
     if ($signame) {
       $sigmsg = "Shutting down in response to signal {$signo} ({$signame}).";
     } else {
       $sigmsg = "Shutting down in response to signal {$signo}.";
     }
 
     $this->logMessage('EXIT', $sigmsg, $signo);
     $this->annihilateProcessGroup();
     $this->dispatchEvent(self::EVENT_WILL_EXIT);
   }
 
   private function logMessage($type, $message, $context = null) {
     if (!$this->getSilent()) {
       echo date('Y-m-d g:i:s A').' ['.$type.'] '.$message."\n";
     }
 
     $this->dispatchEvent(
       self::EVENT_DID_LOG,
       array(
         'type' => $type,
         'message' => $message,
         'context' => $context,
       ));
   }
 
 }
diff --git a/src/daemon/PhutilDaemonOverseer.php b/src/daemon/PhutilDaemonOverseer.php
index 5915265..e2ec3be 100644
--- a/src/daemon/PhutilDaemonOverseer.php
+++ b/src/daemon/PhutilDaemonOverseer.php
@@ -1,339 +1,453 @@
 <?php
 
 /**
  * Oversees a daemon and restarts it if it fails.
  */
 final class PhutilDaemonOverseer {
 
   private $argv;
   private $moreArgs;
   private $inAbruptShutdown;
   private $inGracefulShutdown;
   private static $instance;
 
   private $config;
   private $daemons = array();
   private $traceMode;
   private $traceMemory;
   private $daemonize;
   private $piddir;
   private $log;
   private $libraries = array();
   private $verbose;
   private $err = 0;
   private $lastPidfile;
   private $startEpoch;
+  private $autoscale = array();
 
   public function __construct(array $argv) {
     PhutilServiceProfiler::getInstance()->enableDiscardMode();
 
     $args = new PhutilArgumentParser($argv);
     $args->setTagline('daemon overseer');
     $args->setSynopsis(<<<EOHELP
 **launch_daemon.php** [__options__] __daemon__
     Launch and oversee an instance of __daemon__.
 EOHELP
       );
     $args->parseStandardArguments();
     $args->parse(
       array(
         array(
           'name' => 'trace-memory',
           'help' => 'Enable debug memory tracing.',
         ),
         array(
           'name'  => 'verbose',
           'help'  => 'Enable verbose activity logging.',
         ),
         array(
           'name' => 'label',
           'short' => 'l',
           'param' => 'label',
           'help' => pht(
             'Optional process label. Makes "ps" nicer, no behavioral effects.'),
         ),
       ));
     $argv = array();
 
     if ($args->getArg('trace')) {
       $this->traceMode = true;
       $argv[] = '--trace';
     }
 
     if ($args->getArg('trace-memory')) {
       $this->traceMode = true;
       $this->traceMemory = true;
       $argv[] = '--trace-memory';
     }
     $verbose = $args->getArg('verbose');
     if ($verbose) {
       $this->verbose = true;
       $argv[] = '--verbose';
     }
 
     $label = $args->getArg('label');
     if ($label) {
       $argv[] = '-l';
       $argv[] = $label;
     }
 
     $this->argv = $argv;
 
     if (function_exists('posix_isatty') && posix_isatty(STDIN)) {
       fprintf(STDERR, pht('Reading daemon configuration from stdin...')."\n");
     }
     $config = @file_get_contents('php://stdin');
     $config = id(new PhutilJSONParser())->parse($config);
 
     $this->libraries = idx($config, 'load');
     $this->log = idx($config, 'log');
     $this->daemonize = idx($config, 'daemonize');
     $this->piddir = idx($config, 'piddir');
 
     $this->config = $config;
 
     if (self::$instance) {
       throw new Exception(
         'You may not instantiate more than one Overseer per process.');
     }
 
     self::$instance = $this;
 
     $this->startEpoch = time();
 
     // Check this before we daemonize, since if it's an issue the child will
     // exit immediately.
     if ($this->piddir) {
       $dir = $this->piddir;
       try {
         Filesystem::assertWritable($dir);
       } catch (Exception $ex) {
         throw new Exception(
           "Specified daemon PID directory ('{$dir}') does not exist or is ".
           "not writable by the daemon user!");
       }
     }
 
     if (!idx($config, 'daemons')) {
       throw new PhutilArgumentUsageException(
         pht('You must specify at least one daemon to start!'));
     }
 
     if ($this->log) {
       // NOTE: Now that we're committed to daemonizing, redirect the error
       // log if we have a `--log` parameter. Do this at the last moment
       // so as many setup issues as possible are surfaced.
       ini_set('error_log', $this->log);
     }
 
     if ($this->daemonize) {
       // We need to get rid of these or the daemon will hang when we TERM it
       // waiting for something to read the buffers. TODO: Learn how unix works.
       fclose(STDOUT);
       fclose(STDERR);
       ob_start();
 
       $pid = pcntl_fork();
       if ($pid === -1) {
         throw new Exception('Unable to fork!');
       } else if ($pid) {
         exit(0);
       }
     }
 
     declare(ticks = 1);
     pcntl_signal(SIGUSR2, array($this, 'didReceiveNotifySignal'));
 
     pcntl_signal(SIGINT,  array($this, 'didReceiveGracefulSignal'));
     pcntl_signal(SIGTERM, array($this, 'didReceiveTerminalSignal'));
   }
 
   public function addLibrary($library) {
     $this->libraries[] = $library;
     return $this;
   }
 
   public function run() {
     $this->daemons = array();
 
     foreach ($this->config['daemons'] as $config) {
+      $config += array(
+        'argv' => array(),
+        'autoscale' => array(),
+      );
+
       $daemon = new PhutilDaemonHandle(
         $this,
         $config['class'],
         $this->argv,
         array(
           'log' => $this->log,
           'argv' => $config['argv'],
           'load' => $this->libraries,
+          'autoscale' => $config['autoscale'],
         ));
 
       $daemon->setSilent((!$this->traceMode && !$this->verbose));
       $daemon->setTraceMemory($this->traceMemory);
 
-      $this->daemons[] = array(
-        'config' => $config,
-        'handle' => $daemon,
-      );
+      $this->addDaemon($daemon, $config);
     }
 
     while (true) {
       $futures = array();
       foreach ($this->getDaemonHandles() as $daemon) {
         $daemon->update();
         if ($daemon->isRunning()) {
           $futures[] = $daemon->getFuture();
         }
+
+        if ($daemon->isDone()) {
+          $this->removeDaemon($daemon);
+        }
       }
 
       $this->updatePidfile();
+      $this->updateAutoscale();
 
       if ($futures) {
         $iter = id(new FutureIterator($futures))
           ->setUpdateInterval(1);
         foreach ($iter as $future) {
           break;
         }
       } else {
         if ($this->inGracefulShutdown) {
           break;
         }
         sleep(1);
       }
     }
 
     exit($this->err);
   }
 
+  private function addDaemon(PhutilDaemonHandle $daemon, array $config) {
+    $id = $daemon->getDaemonID();
+    $this->daemons[$id] = array(
+      'handle' => $daemon,
+      'config' => $config,
+    );
+
+    $autoscale_group = $this->getAutoscaleGroup($daemon);
+    if ($autoscale_group) {
+      $this->autoscale[$autoscale_group][$id] = true;
+    }
+
+    return $this;
+  }
+
+  private function removeDaemon(PhutilDaemonHandle $daemon) {
+    $id = $daemon->getDaemonID();
+
+    $autoscale_group = $this->getAutoscaleGroup($daemon);
+    if ($autoscale_group) {
+      unset($this->autoscale[$autoscale_group][$id]);
+    }
+
+    unset($this->daemons[$id]);
+
+    return $this;
+  }
+
+  private function getAutoscaleGroup(PhutilDaemonHandle $daemon) {
+    return $this->getAutoscaleProperty($daemon, 'group');
+  }
+
+  private function getAutoscaleProperty(
+    PhutilDaemonHandle $daemon,
+    $key,
+    $default = null) {
+
+    $id = $daemon->getDaemonID();
+    $autoscale = $this->daemons[$id]['config']['autoscale'];
+    return idx($autoscale, $key, $default);
+  }
+
+  public function didBeginWork(PhutilDaemonHandle $daemon) {
+    $id = $daemon->getDaemonID();
+    $busy = idx($this->daemons[$daemon->getDaemonID()], 'busy');
+    if (!$busy) {
+      $this->daemons[$id]['busy'] = time();
+    }
+  }
+
+  public function didBeginIdle(PhutilDaemonHandle $daemon) {
+    $id = $daemon->getDaemonID();
+    unset($this->daemons[$id]['busy']);
+  }
+
+  public function updateAutoscale() {
+    foreach ($this->autoscale as $group => $daemons) {
+      $daemon = $this->daemons[head_key($daemons)]['handle'];
+      $scaleup_duration = $this->getAutoscaleProperty($daemon, 'up', 2);
+      $max_pool_size = $this->getAutoscaleProperty($daemon, 'pool', 8);
+
+      // Don't scale a group if it is already at the maximum pool size.
+      if (count($daemons) >= $max_pool_size) {
+        continue;
+      }
+
+      $should_scale = true;
+      foreach ($daemons as $daemon_id => $ignored) {
+        $busy = idx($this->daemons[$daemon_id], 'busy');
+        if (!$busy) {
+          // At least one daemon in the group hasn't reported that it has
+          // started work.
+          $should_scale = false;
+          break;
+        }
+
+        if ((time() - $busy) < $scaleup_duration) {
+          // At least one daemon in the group was idle recently, so we have
+          // not fullly
+          $should_scale = false;
+          break;
+        }
+      }
+
+      if ($should_scale) {
+        $config = $this->daemons[$daemon_id]['config'];
+
+        $config['autoscale']['clone'] = true;
+
+        $clone = new PhutilDaemonHandle(
+          $this,
+          $config['class'],
+          $this->argv,
+          array(
+            'log' => $this->log,
+            'argv' => $config['argv'],
+            'load' => $this->libraries,
+            'autoscale' => $config['autoscale'],
+          ));
+
+        $this->addDaemon($clone, $config);
+      }
+    }
+  }
+
   public function didReceiveNotifySignal($signo) {
     foreach ($this->getDaemonHandles() as $daemon) {
       $daemon->didReceiveNotifySignal($signo);
     }
   }
 
   public function didReceiveGracefulSignal($signo) {
     // If we receive SIGINT more than once, interpret it like SIGTERM.
     if ($this->inGracefulShutdown) {
       return $this->didReceiveTerminalSignal($signo);
     }
     $this->inGracefulShutdown = true;
 
     foreach ($this->getDaemonHandles() as $daemon) {
       $daemon->didReceiveGracefulSignal($signo);
     }
   }
 
   public function didReceiveTerminalSignal($signo) {
     $this->err = 128 + $signo;
     if ($this->inAbruptShutdown) {
       exit($this->err);
     }
     $this->inAbruptShutdown = true;
 
     foreach ($this->getDaemonHandles() as $daemon) {
       $daemon->didReceiveTerminalSignal($signo);
     }
   }
 
   private function getDaemonHandles() {
     return ipull($this->daemons, 'handle');
   }
 
   /**
    * Identify running daemons by examining the process table. This isn't
    * completely reliable, but can be used as a fallback if the pid files fail
    * or we end up with stray daemons by other means.
    *
    * Example output (array keys are process IDs):
    *
    *   array(
    *     12345 => array(
    *       'type' => 'overseer',
    *       'command' => 'php launch_daemon.php --daemonize ...',
    *       'pid' => 12345,
    *     ),
    *     12346 => array(
    *       'type' => 'daemon',
    *       'command' => 'php exec_daemon.php ...',
    *       'pid' => 12346,
    *     ),
    *  );
    *
    * @return dict   Map of PIDs to process information, identifying running
    *                daemon processes.
    */
   public static function findRunningDaemons() {
     $results = array();
 
     list($err, $processes) = exec_manual('ps -o pid,command -a -x -w -w -w');
     if ($err) {
       return $results;
     }
 
     $processes = array_filter(explode("\n", trim($processes)));
     foreach ($processes as $process) {
       list($pid, $command) = preg_split('/\s+/', trim($process), 2);
 
       $pattern = '/((launch|exec)_daemon.php|phd-daemon)/';
       $matches = null;
       if (!preg_match($pattern, $command, $matches)) {
         continue;
       }
 
       switch ($matches[1]) {
         case 'exec_daemon.php':
           $type = 'daemon';
           break;
         case 'launch_daemon.php':
         case 'phd-daemon':
         default:
           $type = 'overseer';
           break;
       }
 
       $results[(int)$pid] = array(
         'type' => $type,
         'command' => $command,
         'pid' => (int) $pid,
       );
     }
 
     return $results;
   }
 
   private function updatePidfile() {
     if (!$this->piddir) {
       return;
     }
 
     $daemons = array();
 
     foreach ($this->daemons as $daemon) {
       $handle = $daemon['handle'];
       $config = $daemon['config'];
 
       if (!$handle->isRunning()) {
         continue;
       }
 
       $daemons[] = array(
         'pid' => $handle->getPID(),
         'id' => $handle->getDaemonID(),
         'config' => $config,
       );
     }
 
     $pidfile = array(
       'pid' => getmypid(),
       'start' => $this->startEpoch,
       'config' => $this->config,
       'daemons' => $daemons,
     );
 
     if ($pidfile !== $this->lastPidfile) {
       $this->lastPidfile = $pidfile;
       $pidfile_path = $this->piddir.'/daemon.'.getmypid();
       Filesystem::writeFile($pidfile_path, json_encode($pidfile));
     }
   }
 
 }
diff --git a/src/daemon/torture/PhutilHighIntensityIntervalDaemon.php b/src/daemon/torture/PhutilHighIntensityIntervalDaemon.php
new file mode 100644
index 0000000..469f7f8
--- /dev/null
+++ b/src/daemon/torture/PhutilHighIntensityIntervalDaemon.php
@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * Daemon which is very busy every other minute. This will cause it to
+ * autoscale up and down.
+ *
+ */
+final class PhutilHighIntensityIntervalDaemon extends PhutilTortureTestDaemon {
+
+  protected function run() {
+    while (!$this->shouldExit()) {
+      $m = (int)date('i');
+      if ($m % 2) {
+        $this->willBeginWork();
+        $this->log('Busy.');
+      } else {
+        $this->willBeginIdle();
+        $this->log('Idle.');
+      }
+      $this->sleep(1);
+    }
+  }
+
+}