diff --git a/resources/celerity/map.php b/resources/celerity/map.php
index 9bc0e1b099..e4921a5081 100644
--- a/resources/celerity/map.php
+++ b/resources/celerity/map.php
@@ -1,2443 +1,2443 @@
 <?php
 
 /**
  * This file is automatically generated. Use 'bin/celerity map' to rebuild it.
  *
  * @generated
  */
 return array(
   'names' => array(
     'conpherence.pkg.css' => '0e3cf785',
     'conpherence.pkg.js' => '020aebcf',
     'core.pkg.css' => '00a2e7f4',
     'core.pkg.js' => 'd2de90d9',
     'dark-console.pkg.js' => '187792c2',
     'differential.pkg.css' => 'ffb69e3d',
-    'differential.pkg.js' => 'e31329dc',
+    'differential.pkg.js' => 'c60bec1b',
     'diffusion.pkg.css' => '42c75c37',
     'diffusion.pkg.js' => '78c9885d',
     'maniphest.pkg.css' => '35995d6d',
     'maniphest.pkg.js' => 'c9308721',
     'rsrc/audio/basic/alert.mp3' => '17889334',
     'rsrc/audio/basic/bing.mp3' => 'a817a0c3',
     'rsrc/audio/basic/pock.mp3' => '0fa843d0',
     'rsrc/audio/basic/tap.mp3' => '02d16994',
     'rsrc/audio/basic/ting.mp3' => 'a6b6540e',
     'rsrc/css/aphront/aphront-bars.css' => '4a327b4a',
     'rsrc/css/aphront/dark-console.css' => '7f06cda2',
     'rsrc/css/aphront/dialog-view.css' => '6f4ea703',
     'rsrc/css/aphront/list-filter-view.css' => 'feb64255',
     'rsrc/css/aphront/multi-column.css' => 'fbc00ba3',
     'rsrc/css/aphront/notification.css' => '30240bd2',
     'rsrc/css/aphront/panel-view.css' => '46923d46',
     'rsrc/css/aphront/phabricator-nav-view.css' => '423f92cc',
     'rsrc/css/aphront/table-view.css' => '0bb61df1',
     'rsrc/css/aphront/tokenizer.css' => '34e2a838',
     'rsrc/css/aphront/tooltip.css' => 'e3f2412f',
     'rsrc/css/aphront/typeahead-browse.css' => 'b7ed02d2',
     'rsrc/css/aphront/typeahead.css' => '8779483d',
     'rsrc/css/application/almanac/almanac.css' => '2e050f4f',
     'rsrc/css/application/auth/auth.css' => 'c2f23d74',
     'rsrc/css/application/base/main-menu-view.css' => 'bcec20f0',
     'rsrc/css/application/base/notification-menu.css' => '4df1ee30',
     'rsrc/css/application/base/phui-theme.css' => '35883b37',
     'rsrc/css/application/base/standard-page-view.css' => 'a374f94c',
     'rsrc/css/application/chatlog/chatlog.css' => 'abdc76ee',
     'rsrc/css/application/conduit/conduit-api.css' => 'ce2cfc41',
     'rsrc/css/application/config/config-options.css' => '16c920ae',
     'rsrc/css/application/config/config-template.css' => '20babf50',
     'rsrc/css/application/config/setup-issue.css' => '5eed85b2',
     'rsrc/css/application/config/unhandled-exception.css' => '9ecfc00d',
     'rsrc/css/application/conpherence/color.css' => 'b17746b0',
     'rsrc/css/application/conpherence/durable-column.css' => '2d57072b',
     'rsrc/css/application/conpherence/header-pane.css' => 'c9a3db8e',
     'rsrc/css/application/conpherence/menu.css' => '67f4680d',
     'rsrc/css/application/conpherence/message-pane.css' => 'd244db1e',
     'rsrc/css/application/conpherence/notification.css' => '6a3d4e58',
     'rsrc/css/application/conpherence/participant-pane.css' => '69e0058a',
     'rsrc/css/application/conpherence/transaction.css' => '3a3f5e7e',
     'rsrc/css/application/contentsource/content-source-view.css' => 'cdf0d579',
     'rsrc/css/application/countdown/timer.css' => 'bff8012f',
     'rsrc/css/application/daemon/bulk-job.css' => '73af99f5',
     'rsrc/css/application/dashboard/dashboard.css' => '5a205b9d',
     'rsrc/css/application/diff/diff-tree-view.css' => 'e2d3e222',
     'rsrc/css/application/diff/inline-comment-summary.css' => '81eb368d',
     'rsrc/css/application/differential/add-comment.css' => '7e5900d9',
     'rsrc/css/application/differential/changeset-view.css' => '60c3d405',
     'rsrc/css/application/differential/core.css' => '7300a73e',
     'rsrc/css/application/differential/phui-inline-comment.css' => '9863a85e',
     'rsrc/css/application/differential/revision-comment.css' => '7dbc8d1d',
     'rsrc/css/application/differential/revision-history.css' => '237a2979',
     'rsrc/css/application/differential/revision-list.css' => '93d2df7d',
     'rsrc/css/application/differential/table-of-contents.css' => 'bba788b9',
     'rsrc/css/application/diffusion/diffusion-icons.css' => '23b31a1b',
     'rsrc/css/application/diffusion/diffusion-readme.css' => 'b68a76e4',
     'rsrc/css/application/diffusion/diffusion-repository.css' => 'b89e8c6c',
     'rsrc/css/application/diffusion/diffusion.css' => 'e46232d6',
     'rsrc/css/application/feed/feed.css' => 'd8b6e3f8',
     'rsrc/css/application/files/global-drag-and-drop.css' => '1d2713a4',
     'rsrc/css/application/flag/flag.css' => '2b77be8d',
     'rsrc/css/application/harbormaster/harbormaster.css' => '8dfe16b2',
     'rsrc/css/application/herald/herald-test.css' => '7e7bbdae',
     'rsrc/css/application/herald/herald.css' => '648d39e2',
     'rsrc/css/application/maniphest/report.css' => '3d53188b',
     'rsrc/css/application/maniphest/task-edit.css' => '272daa84',
     'rsrc/css/application/maniphest/task-summary.css' => '61d1667e',
     'rsrc/css/application/objectselector/object-selector.css' => 'ee77366f',
     'rsrc/css/application/owners/owners-path-editor.css' => 'fa7c13ef',
     'rsrc/css/application/paste/paste.css' => 'b37bcd38',
     'rsrc/css/application/people/people-picture-menu-item.css' => 'fe8e07cf',
     'rsrc/css/application/people/people-profile.css' => '2ea2daa1',
     'rsrc/css/application/phame/phame.css' => 'bb442327',
     'rsrc/css/application/pholio/pholio-edit.css' => '4df55b3b',
     'rsrc/css/application/pholio/pholio-inline-comments.css' => '722b48c2',
     'rsrc/css/application/pholio/pholio.css' => '88ef5ef1',
     'rsrc/css/application/phortune/phortune-credit-card-form.css' => '3b9868a8',
     'rsrc/css/application/phortune/phortune-invoice.css' => '4436b241',
     'rsrc/css/application/phortune/phortune.css' => '508a1a5e',
     'rsrc/css/application/phrequent/phrequent.css' => 'bd79cc67',
     'rsrc/css/application/phriction/phriction-document-css.css' => '03380da0',
     'rsrc/css/application/policy/policy-edit.css' => '8794e2ed',
     'rsrc/css/application/policy/policy-transaction-detail.css' => 'c02b8384',
     'rsrc/css/application/policy/policy.css' => 'ceb56a08',
     'rsrc/css/application/ponder/ponder-view.css' => '05a09d0a',
     'rsrc/css/application/project/project-card-view.css' => 'a9f2c2dd',
     'rsrc/css/application/project/project-triggers.css' => 'cd9c8bb9',
     'rsrc/css/application/project/project-view.css' => '567858b3',
     'rsrc/css/application/search/application-search-view.css' => '0f7c06d8',
     'rsrc/css/application/search/search-results.css' => '9ea70ace',
     'rsrc/css/application/slowvote/slowvote.css' => '1694baed',
     'rsrc/css/application/tokens/tokens.css' => 'ce5a50bd',
     'rsrc/css/application/uiexample/example.css' => 'b4795059',
     'rsrc/css/core/core.css' => 'b3ebd90d',
     'rsrc/css/core/remarkup.css' => '5baa3bd9',
     'rsrc/css/core/syntax.css' => '548567f6',
     'rsrc/css/core/z-index.css' => 'ac3bfcd4',
     'rsrc/css/diviner/diviner-shared.css' => '4bd263b0',
     'rsrc/css/font/font-awesome.css' => '3883938a',
     'rsrc/css/font/font-lato.css' => '23631304',
     'rsrc/css/font/phui-font-icon-base.css' => '303c9b87',
     'rsrc/css/fuel/fuel-grid.css' => '66697240',
     'rsrc/css/fuel/fuel-handle-list.css' => '2c4cbeca',
     'rsrc/css/fuel/fuel-map.css' => 'd6e31510',
     'rsrc/css/fuel/fuel-menu.css' => '21f5d199',
     'rsrc/css/layout/phabricator-source-code-view.css' => '03d7ac28',
     'rsrc/css/phui/button/phui-button-bar.css' => 'a4aa75c4',
     'rsrc/css/phui/button/phui-button-simple.css' => '1ff278aa',
     'rsrc/css/phui/button/phui-button.css' => 'ea704902',
     'rsrc/css/phui/calendar/phui-calendar-day.css' => '9597d706',
     'rsrc/css/phui/calendar/phui-calendar-list.css' => 'ccd7e4e2',
     'rsrc/css/phui/calendar/phui-calendar-month.css' => 'cb758c42',
     'rsrc/css/phui/calendar/phui-calendar.css' => 'f11073aa',
     'rsrc/css/phui/object-item/phui-oi-big-ui.css' => 'fa74cc35',
     'rsrc/css/phui/object-item/phui-oi-color.css' => 'b517bfa0',
     'rsrc/css/phui/object-item/phui-oi-drag-ui.css' => 'da15d3dc',
     'rsrc/css/phui/object-item/phui-oi-flush-ui.css' => '490e2e2e',
     'rsrc/css/phui/object-item/phui-oi-list-view.css' => 'af98a277',
     'rsrc/css/phui/object-item/phui-oi-simple-ui.css' => '6a30fa46',
     'rsrc/css/phui/phui-action-list.css' => '1b0085b2',
     'rsrc/css/phui/phui-action-panel.css' => '6c386cbf',
     'rsrc/css/phui/phui-badge.css' => '666e25ad',
     'rsrc/css/phui/phui-basic-nav-view.css' => '56ebd66d',
     'rsrc/css/phui/phui-big-info-view.css' => '362ad37b',
     'rsrc/css/phui/phui-box.css' => '5ed3b8cb',
     'rsrc/css/phui/phui-bulk-editor.css' => '374d5e30',
     'rsrc/css/phui/phui-chart.css' => '14df9ae3',
     'rsrc/css/phui/phui-cms.css' => '8c05c41e',
     'rsrc/css/phui/phui-comment-form.css' => '68a2d99a',
     'rsrc/css/phui/phui-comment-panel.css' => 'ec4e31c0',
     'rsrc/css/phui/phui-crumbs-view.css' => '614f43cf',
     'rsrc/css/phui/phui-curtain-object-ref-view.css' => '5f752bdb',
     'rsrc/css/phui/phui-curtain-view.css' => '68c5efb6',
     'rsrc/css/phui/phui-document-pro.css' => 'b9613a10',
     'rsrc/css/phui/phui-document-summary.css' => 'b068eed1',
     'rsrc/css/phui/phui-document.css' => '52b748a5',
     'rsrc/css/phui/phui-feed-story.css' => 'a0c05029',
     'rsrc/css/phui/phui-fontkit.css' => '1ec937e5',
     'rsrc/css/phui/phui-form-view.css' => '01b796c0',
     'rsrc/css/phui/phui-form.css' => '1f177cb7',
     'rsrc/css/phui/phui-formation-view.css' => 'd2dec8ed',
     'rsrc/css/phui/phui-head-thing.css' => 'd7f293df',
     'rsrc/css/phui/phui-header-view.css' => '36c86a58',
     'rsrc/css/phui/phui-hovercard.css' => '6ca90fa0',
     'rsrc/css/phui/phui-icon-set-selector.css' => '7aa5f3ec',
     'rsrc/css/phui/phui-icon.css' => '4cbc684a',
     'rsrc/css/phui/phui-image-mask.css' => '62c7f4d2',
     'rsrc/css/phui/phui-info-view.css' => 'a10a909b',
     'rsrc/css/phui/phui-invisible-character-view.css' => 'c694c4a4',
     'rsrc/css/phui/phui-left-right.css' => '68513c34',
     'rsrc/css/phui/phui-lightbox.css' => '4ebf22da',
     'rsrc/css/phui/phui-list.css' => '0c04affd',
     'rsrc/css/phui/phui-object-box.css' => 'b8d7eea0',
     'rsrc/css/phui/phui-pager.css' => 'd022c7ad',
     'rsrc/css/phui/phui-pinboard-view.css' => '1f08f5d8',
     'rsrc/css/phui/phui-policy-section-view.css' => '139fdc64',
     'rsrc/css/phui/phui-property-list-view.css' => '5adf7078',
     'rsrc/css/phui/phui-remarkup-preview.css' => '91767007',
     'rsrc/css/phui/phui-segment-bar-view.css' => '5166b370',
     'rsrc/css/phui/phui-spacing.css' => 'b05cadc3',
     'rsrc/css/phui/phui-status.css' => '293b5dad',
     'rsrc/css/phui/phui-tag-view.css' => 'fb811341',
     'rsrc/css/phui/phui-timeline-view.css' => '2d32d7a9',
     'rsrc/css/phui/phui-two-column-view.css' => 'f96d319f',
     'rsrc/css/phui/workboards/phui-workboard-color.css' => 'e86de308',
     'rsrc/css/phui/workboards/phui-workboard.css' => '74fc9d98',
     'rsrc/css/phui/workboards/phui-workcard.css' => '913441b6',
     'rsrc/css/phui/workboards/phui-workpanel.css' => '3ae89b20',
     'rsrc/css/sprite-login.css' => '18b368a6',
     'rsrc/css/sprite-tokens.css' => 'f1896dc5',
     'rsrc/css/syntax/syntax-default.css' => '055fc231',
     'rsrc/externals/d3/d3.min.js' => '9d068042',
     'rsrc/externals/font/fontawesome/fontawesome-webfont.eot' => '23f8c698',
     'rsrc/externals/font/fontawesome/fontawesome-webfont.ttf' => '70983df0',
     'rsrc/externals/font/fontawesome/fontawesome-webfont.woff' => 'cd02f93b',
     'rsrc/externals/font/fontawesome/fontawesome-webfont.woff2' => '351fd46a',
     'rsrc/externals/font/lato/lato-bold.eot' => '7367aa5e',
     'rsrc/externals/font/lato/lato-bold.svg' => '681aa4f5',
     'rsrc/externals/font/lato/lato-bold.ttf' => '66d3c296',
     'rsrc/externals/font/lato/lato-bold.woff' => '89d9fba7',
     'rsrc/externals/font/lato/lato-bold.woff2' => '389fcdb1',
     'rsrc/externals/font/lato/lato-bolditalic.eot' => '03eeb4da',
     'rsrc/externals/font/lato/lato-bolditalic.svg' => 'f56fa11c',
     'rsrc/externals/font/lato/lato-bolditalic.ttf' => '9c3aec21',
     'rsrc/externals/font/lato/lato-bolditalic.woff' => 'bfbd0616',
     'rsrc/externals/font/lato/lato-bolditalic.woff2' => 'bc7d1274',
     'rsrc/externals/font/lato/lato-italic.eot' => '7db5b247',
     'rsrc/externals/font/lato/lato-italic.svg' => 'b1ae496f',
     'rsrc/externals/font/lato/lato-italic.ttf' => '43eed813',
     'rsrc/externals/font/lato/lato-italic.woff' => 'c28975e1',
     'rsrc/externals/font/lato/lato-italic.woff2' => 'fffc0d8c',
     'rsrc/externals/font/lato/lato-regular.eot' => '06e0c291',
     'rsrc/externals/font/lato/lato-regular.svg' => '3ad95f53',
     'rsrc/externals/font/lato/lato-regular.ttf' => 'e2e9c398',
     'rsrc/externals/font/lato/lato-regular.woff' => '0b13d332',
     'rsrc/externals/font/lato/lato-regular.woff2' => '8f846797',
     'rsrc/externals/javelin/core/Event.js' => 'c03f2fb4',
     'rsrc/externals/javelin/core/Stratcom.js' => '0889b835',
     'rsrc/externals/javelin/core/__tests__/event-stop-and-kill.js' => '048472d2',
     'rsrc/externals/javelin/core/__tests__/install.js' => '14a7e671',
     'rsrc/externals/javelin/core/__tests__/stratcom.js' => 'a28464bb',
     'rsrc/externals/javelin/core/__tests__/util.js' => 'e29a4354',
     'rsrc/externals/javelin/core/init.js' => '98e6504a',
     'rsrc/externals/javelin/core/init_node.js' => '16961339',
     'rsrc/externals/javelin/core/install.js' => '5902260c',
     'rsrc/externals/javelin/core/util.js' => 'edb4d8c9',
     'rsrc/externals/javelin/docs/Base.js' => '5a401d7d',
     'rsrc/externals/javelin/docs/onload.js' => 'ee58fb62',
     'rsrc/externals/javelin/ext/fx/Color.js' => '78f811c9',
     'rsrc/externals/javelin/ext/fx/FX.js' => '34450586',
     'rsrc/externals/javelin/ext/reactor/core/DynVal.js' => '202a2e85',
     'rsrc/externals/javelin/ext/reactor/core/Reactor.js' => '1c850a26',
     'rsrc/externals/javelin/ext/reactor/core/ReactorNode.js' => '72960bc1',
     'rsrc/externals/javelin/ext/reactor/core/ReactorNodeCalmer.js' => '225bbb98',
     'rsrc/externals/javelin/ext/reactor/dom/RDOM.js' => '6cfa0008',
     'rsrc/externals/javelin/ext/view/HTMLView.js' => 'f8c4e135',
     'rsrc/externals/javelin/ext/view/View.js' => '289bf236',
     'rsrc/externals/javelin/ext/view/ViewInterpreter.js' => '876506b6',
     'rsrc/externals/javelin/ext/view/ViewPlaceholder.js' => 'a9942052',
     'rsrc/externals/javelin/ext/view/ViewRenderer.js' => '9aae2b66',
     'rsrc/externals/javelin/ext/view/ViewVisitor.js' => '308f9fe4',
     'rsrc/externals/javelin/ext/view/__tests__/HTMLView.js' => '6e50a13f',
     'rsrc/externals/javelin/ext/view/__tests__/View.js' => 'd284be5d',
     'rsrc/externals/javelin/ext/view/__tests__/ViewInterpreter.js' => 'a9f35511',
     'rsrc/externals/javelin/ext/view/__tests__/ViewRenderer.js' => '3a1b81f6',
     'rsrc/externals/javelin/lib/Cookie.js' => '05d290ef',
     'rsrc/externals/javelin/lib/DOM.js' => 'e4c7622a',
     'rsrc/externals/javelin/lib/History.js' => '030b4f7a',
     'rsrc/externals/javelin/lib/JSON.js' => '541f81c3',
     'rsrc/externals/javelin/lib/Leader.js' => '0d2490ce',
     'rsrc/externals/javelin/lib/Mask.js' => '7c4d8998',
     'rsrc/externals/javelin/lib/Quicksand.js' => 'd3799cb4',
     'rsrc/externals/javelin/lib/Request.js' => '84e6891f',
     'rsrc/externals/javelin/lib/Resource.js' => '20514cc2',
     'rsrc/externals/javelin/lib/Routable.js' => '6a18c42e',
     'rsrc/externals/javelin/lib/Router.js' => '32755edb',
     'rsrc/externals/javelin/lib/Scrollbar.js' => 'a43ae2ae',
     'rsrc/externals/javelin/lib/Sound.js' => 'd4cc2d2a',
     'rsrc/externals/javelin/lib/URI.js' => '2e255291',
     'rsrc/externals/javelin/lib/Vector.js' => 'e9c80beb',
     'rsrc/externals/javelin/lib/WebSocket.js' => 'fdc13e4e',
     'rsrc/externals/javelin/lib/Workflow.js' => '945ff654',
     'rsrc/externals/javelin/lib/__tests__/Cookie.js' => 'ca686f71',
     'rsrc/externals/javelin/lib/__tests__/DOM.js' => '4566e249',
     'rsrc/externals/javelin/lib/__tests__/JSON.js' => '710377ae',
     'rsrc/externals/javelin/lib/__tests__/URI.js' => '6fff0c2b',
     'rsrc/externals/javelin/lib/__tests__/behavior.js' => '8426ebeb',
     'rsrc/externals/javelin/lib/behavior.js' => '1b6acc2a',
     'rsrc/externals/javelin/lib/control/tokenizer/Tokenizer.js' => '89a1ae3a',
     'rsrc/externals/javelin/lib/control/typeahead/Typeahead.js' => 'a4356cde',
     'rsrc/externals/javelin/lib/control/typeahead/normalizer/TypeaheadNormalizer.js' => 'a241536a',
     'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadCompositeSource.js' => '22ee68a5',
     'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadOnDemandSource.js' => '23387297',
     'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadPreloadedSource.js' => '5a79f6c3',
     'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadSource.js' => '8badee71',
     'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadStaticSource.js' => '80bff3af',
     'rsrc/favicons/favicon-16x16.png' => '4c51a03a',
     'rsrc/favicons/mask-icon.svg' => 'db699fe1',
     'rsrc/image/BFCFDA.png' => '74b5c88b',
     'rsrc/image/actions/edit.png' => 'fd987dff',
     'rsrc/image/avatar.png' => '0d17c6c4',
     'rsrc/image/checker_dark.png' => '7fc8fa7b',
     'rsrc/image/checker_light.png' => '3157a202',
     'rsrc/image/checker_lighter.png' => 'c45928c1',
     'rsrc/image/chevron-in.png' => '1aa2f88f',
     'rsrc/image/chevron-out.png' => 'c815e272',
     'rsrc/image/controls/checkbox-checked.png' => '1770d7a0',
     'rsrc/image/controls/checkbox-unchecked.png' => 'e1deba0a',
     'rsrc/image/d5d8e1.png' => '6764616e',
     'rsrc/image/darkload.gif' => '5bd41a89',
     'rsrc/image/divot.png' => '0fbe2453',
     'rsrc/image/examples/hero.png' => '5d8c4b21',
     'rsrc/image/grippy_texture.png' => 'a7d222b5',
     'rsrc/image/icon/fatcow/arrow_branch.png' => '98149d9f',
     'rsrc/image/icon/fatcow/arrow_merge.png' => 'e142f4f8',
     'rsrc/image/icon/fatcow/calendar_edit.png' => '5ff44a08',
     'rsrc/image/icon/fatcow/document_black.png' => 'd3515fa5',
     'rsrc/image/icon/fatcow/flag_blue.png' => '54db2e5c',
     'rsrc/image/icon/fatcow/flag_finish.png' => '2953a51b',
     'rsrc/image/icon/fatcow/flag_ghost.png' => '7d9ada92',
     'rsrc/image/icon/fatcow/flag_green.png' => '010f7161',
     'rsrc/image/icon/fatcow/flag_orange.png' => '6c384ca5',
     'rsrc/image/icon/fatcow/flag_pink.png' => '11ac6b12',
     'rsrc/image/icon/fatcow/flag_purple.png' => 'c4f423a4',
     'rsrc/image/icon/fatcow/flag_red.png' => '9e6d8817',
     'rsrc/image/icon/fatcow/flag_yellow.png' => '906733f4',
     'rsrc/image/icon/fatcow/key_question.png' => 'c10c26db',
     'rsrc/image/icon/fatcow/link.png' => '8edbf327',
     'rsrc/image/icon/fatcow/page_white_edit.png' => '17ef5625',
     'rsrc/image/icon/fatcow/page_white_put.png' => '82430c91',
     'rsrc/image/icon/fatcow/source/conduit.png' => '5b55130c',
     'rsrc/image/icon/fatcow/source/email.png' => '8a32b77f',
     'rsrc/image/icon/fatcow/source/fax.png' => '8bc2a49b',
     'rsrc/image/icon/fatcow/source/mobile.png' => '0a918412',
     'rsrc/image/icon/fatcow/source/tablet.png' => 'fc50b050',
     'rsrc/image/icon/fatcow/source/web.png' => '70433af3',
     'rsrc/image/icon/subscribe.png' => '07ef454e',
     'rsrc/image/icon/tango/attachment.png' => 'bac9032d',
     'rsrc/image/icon/tango/edit.png' => 'e6296206',
     'rsrc/image/icon/tango/go-down.png' => '0b903712',
     'rsrc/image/icon/tango/log.png' => '86b6a6f4',
     'rsrc/image/icon/tango/upload.png' => '3fe6b92d',
     'rsrc/image/icon/unsubscribe.png' => 'db04378a',
     'rsrc/image/lightblue-header.png' => 'e6d483c6',
     'rsrc/image/logo/light-eye.png' => '72337472',
     'rsrc/image/main_texture.png' => '894d03c4',
     'rsrc/image/menu_texture.png' => '896c9ade',
     'rsrc/image/people/harding.png' => '95b2db63',
     'rsrc/image/people/jefferson.png' => 'e883a3a2',
     'rsrc/image/people/lincoln.png' => 'be2c07c5',
     'rsrc/image/people/mckinley.png' => '6af510a0',
     'rsrc/image/people/taft.png' => 'b15ab07e',
     'rsrc/image/people/user0.png' => '4bc64b40',
     'rsrc/image/people/user1.png' => '8063f445',
     'rsrc/image/people/user2.png' => 'd28246c0',
     'rsrc/image/people/user3.png' => 'fb1ac12d',
     'rsrc/image/people/user4.png' => 'fe4fac8f',
     'rsrc/image/people/user5.png' => '3d07065c',
     'rsrc/image/people/user6.png' => 'e4bd47c8',
     'rsrc/image/people/user7.png' => '71d8fe8b',
     'rsrc/image/people/user8.png' => '85f86bf7',
     'rsrc/image/people/user9.png' => '523db8aa',
     'rsrc/image/people/washington.png' => '86159e68',
     'rsrc/image/phrequent_active.png' => 'de66dc50',
     'rsrc/image/phrequent_inactive.png' => '79c61baf',
     'rsrc/image/resize.png' => '9cc83373',
     'rsrc/image/sprite-login-X2.png' => '604545f6',
     'rsrc/image/sprite-login.png' => '7a001a9a',
     'rsrc/image/sprite-tokens-X2.png' => '21621dd9',
     'rsrc/image/sprite-tokens.png' => 'bede2580',
     'rsrc/image/texture/card-gradient.png' => 'e6892cb4',
     'rsrc/image/texture/dark-menu-hover.png' => '390a4fa1',
     'rsrc/image/texture/dark-menu.png' => '542f699c',
     'rsrc/image/texture/grip.png' => 'bc80753a',
     'rsrc/image/texture/panel-header-gradient.png' => '65004dbf',
     'rsrc/image/texture/phlnx-bg.png' => '6c9cd31d',
     'rsrc/image/texture/pholio-background.gif' => '84910bfc',
     'rsrc/image/texture/table_header.png' => '7652d1ad',
     'rsrc/image/texture/table_header_hover.png' => '12ea5236',
     'rsrc/image/texture/table_header_tall.png' => '5cc420c4',
     'rsrc/js/application/aphlict/Aphlict.js' => '022516b4',
     'rsrc/js/application/aphlict/behavior-aphlict-dropdown.js' => 'e9a2940f',
     'rsrc/js/application/aphlict/behavior-aphlict-listen.js' => '4e61fa88',
     'rsrc/js/application/aphlict/behavior-aphlict-status.js' => 'c3703a16',
     'rsrc/js/application/aphlict/behavior-desktop-notifications-control.js' => '070679fe',
     'rsrc/js/application/calendar/behavior-day-view.js' => '727a5a61',
     'rsrc/js/application/calendar/behavior-event-all-day.js' => '0b1bc990',
     'rsrc/js/application/calendar/behavior-month-view.js' => '158c64e0',
     'rsrc/js/application/config/behavior-reorder-fields.js' => '2539f834',
     'rsrc/js/application/conpherence/ConpherenceThreadManager.js' => 'aec8e38c',
     'rsrc/js/application/conpherence/behavior-conpherence-search.js' => '91befbcc',
     'rsrc/js/application/conpherence/behavior-durable-column.js' => 'fa6f30b2',
     'rsrc/js/application/conpherence/behavior-menu.js' => '8c2ed2bf',
     'rsrc/js/application/conpherence/behavior-participant-pane.js' => '43ba89a2',
     'rsrc/js/application/conpherence/behavior-pontificate.js' => '4ae58b5a',
     'rsrc/js/application/conpherence/behavior-quicksand-blacklist.js' => '5a6f6a06',
     'rsrc/js/application/conpherence/behavior-toggle-widget.js' => '8f959ad0',
     'rsrc/js/application/countdown/timer.js' => '6a162524',
     'rsrc/js/application/daemon/behavior-bulk-job-reload.js' => '3829a3cf',
     'rsrc/js/application/dashboard/behavior-dashboard-async-panel.js' => '9c01e364',
     'rsrc/js/application/dashboard/behavior-dashboard-move-panels.js' => 'a2ab19be',
     'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '1e413dc9',
     'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => '0116d3e8',
     'rsrc/js/application/diff/DiffChangeset.js' => 'd7d3ba75',
     'rsrc/js/application/diff/DiffChangesetList.js' => 'cc2c5de5',
     'rsrc/js/application/diff/DiffInline.js' => '9c775532',
     'rsrc/js/application/diff/DiffInlineContentState.js' => 'aa51efb4',
     'rsrc/js/application/diff/DiffPathView.js' => '8207abf9',
     'rsrc/js/application/diff/DiffTreeView.js' => '5d83623b',
     'rsrc/js/application/differential/behavior-diff-radios.js' => '925fe8cd',
     'rsrc/js/application/differential/behavior-populate.js' => 'b86ef6c2',
     'rsrc/js/application/diffusion/DiffusionLocateFileSource.js' => '94243d89',
     'rsrc/js/application/diffusion/ExternalEditorLinkEngine.js' => '48a8641f',
     'rsrc/js/application/diffusion/behavior-audit-preview.js' => 'b7b73831',
     'rsrc/js/application/diffusion/behavior-commit-branches.js' => '4b671572',
     'rsrc/js/application/diffusion/behavior-commit-graph.js' => 'ac10c917',
     'rsrc/js/application/diffusion/behavior-locate-file.js' => '87428eb2',
     'rsrc/js/application/diffusion/behavior-pull-lastmodified.js' => 'c715c123',
     'rsrc/js/application/doorkeeper/behavior-doorkeeper-tag.js' => '6a85bc5a',
     'rsrc/js/application/drydock/drydock-live-operation-status.js' => '47a0728b',
     'rsrc/js/application/fact/Chart.js' => '52e3ff03',
     'rsrc/js/application/fact/ChartCurtainView.js' => '86954222',
     'rsrc/js/application/fact/ChartFunctionLabel.js' => '81de1dab',
     'rsrc/js/application/files/behavior-document-engine.js' => '243d6c22',
     'rsrc/js/application/files/behavior-icon-composer.js' => '38a6cedb',
     'rsrc/js/application/files/behavior-launch-icon-composer.js' => 'a17b84f1',
     'rsrc/js/application/harbormaster/behavior-harbormaster-log.js' => 'b347a301',
     'rsrc/js/application/herald/HeraldRuleEditor.js' => '2633bef7',
     'rsrc/js/application/herald/PathTypeahead.js' => 'ad486db3',
     'rsrc/js/application/herald/herald-rule-editor.js' => '0922e81d',
     'rsrc/js/application/maniphest/behavior-batch-selector.js' => '139ef688',
     'rsrc/js/application/maniphest/behavior-line-chart.js' => 'ad258e28',
     'rsrc/js/application/maniphest/behavior-list-edit.js' => 'c687e867',
     'rsrc/js/application/owners/OwnersPathEditor.js' => '2a8b62d9',
     'rsrc/js/application/owners/owners-path-editor.js' => 'ff688a7a',
     'rsrc/js/application/passphrase/passphrase-credential-control.js' => '48fe33d0',
     'rsrc/js/application/pholio/behavior-pholio-mock-edit.js' => '3eed1f2b',
     'rsrc/js/application/pholio/behavior-pholio-mock-view.js' => '5aa1544e',
     'rsrc/js/application/phortune/behavior-stripe-payment-form.js' => '02cb4398',
     'rsrc/js/application/phortune/behavior-test-payment-form.js' => '4a7fb02b',
     'rsrc/js/application/phortune/phortune-credit-card-form.js' => 'd12d214f',
     'rsrc/js/application/policy/behavior-policy-control.js' => '0eaa33a9',
     'rsrc/js/application/policy/behavior-policy-rule-editor.js' => '9347f172',
     'rsrc/js/application/projects/WorkboardBoard.js' => 'b46d88c5',
     'rsrc/js/application/projects/WorkboardCard.js' => '0392a5d8',
     'rsrc/js/application/projects/WorkboardCardTemplate.js' => '84f82dad',
     'rsrc/js/application/projects/WorkboardColumn.js' => 'c3d24e63',
     'rsrc/js/application/projects/WorkboardController.js' => 'b9d0c2f3',
     'rsrc/js/application/projects/WorkboardDropEffect.js' => '8e0aa661',
     'rsrc/js/application/projects/WorkboardHeader.js' => '111bfd2d',
     'rsrc/js/application/projects/WorkboardHeaderTemplate.js' => 'ebe83a6b',
     'rsrc/js/application/projects/WorkboardOrderTemplate.js' => '03e8891f',
     'rsrc/js/application/projects/behavior-project-boards.js' => '58cb6a88',
     'rsrc/js/application/projects/behavior-project-create.js' => '34c53422',
     'rsrc/js/application/projects/behavior-reorder-columns.js' => '8ac32fd9',
     'rsrc/js/application/repository/repository-crossreference.js' => '44d48cd1',
     'rsrc/js/application/search/behavior-reorder-profile-menu-items.js' => 'e5bdb730',
     'rsrc/js/application/search/behavior-reorder-queries.js' => 'b86f297f',
     'rsrc/js/application/transactions/behavior-comment-actions.js' => '4dffaeb2',
     'rsrc/js/application/transactions/behavior-reorder-configs.js' => '4842f137',
     'rsrc/js/application/transactions/behavior-reorder-fields.js' => '0ad8d31f',
     'rsrc/js/application/transactions/behavior-show-older-transactions.js' => '8b5c7d65',
     'rsrc/js/application/transactions/behavior-transaction-comment-form.js' => '2bdadf1a',
     'rsrc/js/application/transactions/behavior-transaction-list.js' => '9cec214e',
     'rsrc/js/application/trigger/TriggerRule.js' => '41b7b4f6',
     'rsrc/js/application/trigger/TriggerRuleControl.js' => '5faf27b9',
     'rsrc/js/application/trigger/TriggerRuleEditor.js' => 'b49fd60c',
     'rsrc/js/application/trigger/TriggerRuleType.js' => '4feea7d3',
     'rsrc/js/application/trigger/trigger-rule-editor.js' => '398fdf13',
     'rsrc/js/application/typeahead/behavior-typeahead-browse.js' => '70245195',
     'rsrc/js/application/typeahead/behavior-typeahead-search.js' => '7b139193',
     'rsrc/js/application/uiexample/gesture-example.js' => '242dedd0',
     'rsrc/js/application/uiexample/notification-example.js' => '29819b75',
     'rsrc/js/core/Busy.js' => '5202e831',
     'rsrc/js/core/DragAndDropFileUpload.js' => '4370900d',
     'rsrc/js/core/DraggableList.js' => '0169e425',
     'rsrc/js/core/Favicon.js' => '7930776a',
     'rsrc/js/core/FileUpload.js' => 'ab85e184',
     'rsrc/js/core/Hovercard.js' => '6199f752',
     'rsrc/js/core/HovercardList.js' => 'de4b4919',
     'rsrc/js/core/KeyboardShortcut.js' => '1a844c06',
     'rsrc/js/core/KeyboardShortcutManager.js' => '81debc48',
     'rsrc/js/core/MultirowRowManager.js' => '5b54c823',
     'rsrc/js/core/Notification.js' => 'a9b91e3f',
     'rsrc/js/core/Prefab.js' => '5793d835',
     'rsrc/js/core/ShapedRequest.js' => '995f5102',
     'rsrc/js/core/TextAreaUtils.js' => 'f340a484',
     'rsrc/js/core/Title.js' => '43bc9360',
     'rsrc/js/core/ToolTip.js' => '83754533',
     'rsrc/js/core/behavior-audio-source.js' => '3dc5ad43',
     'rsrc/js/core/behavior-autofocus.js' => '65bb0011',
     'rsrc/js/core/behavior-badge-view.js' => '92cdd7b6',
     'rsrc/js/core/behavior-bulk-editor.js' => 'aa6d2308',
     'rsrc/js/core/behavior-choose-control.js' => '04f8a1e3',
     'rsrc/js/core/behavior-copy.js' => 'cf32921f',
     'rsrc/js/core/behavior-detect-timezone.js' => '78bc5d94',
     'rsrc/js/core/behavior-device.js' => 'ac2b1e01',
-    'rsrc/js/core/behavior-drag-and-drop-textarea.js' => '7df68a45',
+    'rsrc/js/core/behavior-drag-and-drop-textarea.js' => '3277c62d',
     'rsrc/js/core/behavior-fancy-datepicker.js' => '36821f8d',
     'rsrc/js/core/behavior-form.js' => '55d7b788',
     'rsrc/js/core/behavior-gesture.js' => 'b58d1a2a',
     'rsrc/js/core/behavior-global-drag-and-drop.js' => '1cab0e9a',
     'rsrc/js/core/behavior-high-security-warning.js' => 'dae2d55b',
     'rsrc/js/core/behavior-history-install.js' => '6a1583a8',
     'rsrc/js/core/behavior-hovercard.js' => '183738e6',
     'rsrc/js/core/behavior-keyboard-pager.js' => '1325b731',
     'rsrc/js/core/behavior-keyboard-shortcuts.js' => '42c44e8b',
     'rsrc/js/core/behavior-lightbox-attachments.js' => '14c7ab36',
     'rsrc/js/core/behavior-line-linker.js' => '0d915ff5',
     'rsrc/js/core/behavior-linked-container.js' => '74446546',
     'rsrc/js/core/behavior-more.js' => '506aa3f4',
     'rsrc/js/core/behavior-object-selector.js' => '98ef467f',
     'rsrc/js/core/behavior-oncopy.js' => 'da8f5259',
     'rsrc/js/core/behavior-phabricator-remarkup-assist.js' => '54262396',
     'rsrc/js/core/behavior-read-only-warning.js' => 'b9109f8f',
     'rsrc/js/core/behavior-redirect.js' => '407ee861',
     'rsrc/js/core/behavior-refresh-csrf.js' => '46116c01',
     'rsrc/js/core/behavior-remarkup-load-image.js' => '202bfa3f',
     'rsrc/js/core/behavior-remarkup-preview.js' => 'd8a86cfb',
     'rsrc/js/core/behavior-reorder-applications.js' => 'aa371860',
     'rsrc/js/core/behavior-reveal-content.js' => 'b105a3a6',
     'rsrc/js/core/behavior-scrollbar.js' => '92388bae',
     'rsrc/js/core/behavior-search-typeahead.js' => '1cb7d027',
     'rsrc/js/core/behavior-select-content.js' => 'e8240b50',
     'rsrc/js/core/behavior-select-on-click.js' => '66365ee2',
     'rsrc/js/core/behavior-setup-check-https.js' => '01384686',
     'rsrc/js/core/behavior-time-typeahead.js' => '5803b9e7',
     'rsrc/js/core/behavior-toggle-class.js' => '32db8374',
     'rsrc/js/core/behavior-tokenizer.js' => '3b4899b0',
     'rsrc/js/core/behavior-tooltip.js' => '73ecc1f8',
     'rsrc/js/core/behavior-user-menu.js' => '60cd9241',
     'rsrc/js/core/behavior-watch-anchor.js' => 'a77e2cbd',
     'rsrc/js/core/behavior-workflow.js' => '9623adc1',
     'rsrc/js/core/darkconsole/DarkLog.js' => '3b869402',
     'rsrc/js/core/darkconsole/DarkMessage.js' => '26cd4b73',
     'rsrc/js/core/darkconsole/behavior-dark-console.js' => '457f4d16',
     'rsrc/js/core/phtize.js' => '2f1db1ed',
     'rsrc/js/phui/behavior-phui-dropdown-menu.js' => '5cf0501a',
     'rsrc/js/phui/behavior-phui-file-upload.js' => 'e150bd50',
     'rsrc/js/phui/behavior-phui-selectable-list.js' => 'b26a41e4',
     'rsrc/js/phui/behavior-phui-submenu.js' => 'b5e9bff9',
     'rsrc/js/phui/behavior-phui-tab-group.js' => '242aa08b',
     'rsrc/js/phui/behavior-phui-timer-control.js' => 'f84bcbf4',
     'rsrc/js/phuix/PHUIXActionListView.js' => 'c68f183f',
     'rsrc/js/phuix/PHUIXActionView.js' => 'a8f573a9',
     'rsrc/js/phuix/PHUIXAutocomplete.js' => '2fbe234d',
     'rsrc/js/phuix/PHUIXButtonView.js' => '55a24e84',
     'rsrc/js/phuix/PHUIXDropdownMenu.js' => 'b557770a',
     'rsrc/js/phuix/PHUIXExample.js' => 'c2c500a7',
     'rsrc/js/phuix/PHUIXFormControl.js' => '38c1f3fb',
     'rsrc/js/phuix/PHUIXFormationColumnView.js' => '4bcc1f78',
     'rsrc/js/phuix/PHUIXFormationFlankView.js' => '6648270a',
     'rsrc/js/phuix/PHUIXFormationView.js' => 'cef53b3e',
     'rsrc/js/phuix/PHUIXIconView.js' => 'a5257c4e',
   ),
   'symbols' => array(
     'almanac-css' => '2e050f4f',
     'aphront-bars' => '4a327b4a',
     'aphront-dark-console-css' => '7f06cda2',
     'aphront-dialog-view-css' => '6f4ea703',
     'aphront-list-filter-view-css' => 'feb64255',
     'aphront-multi-column-view-css' => 'fbc00ba3',
     'aphront-panel-view-css' => '46923d46',
     'aphront-table-view-css' => '0bb61df1',
     'aphront-tokenizer-control-css' => '34e2a838',
     'aphront-tooltip-css' => 'e3f2412f',
     'aphront-typeahead-control-css' => '8779483d',
     'application-search-view-css' => '0f7c06d8',
     'auth-css' => 'c2f23d74',
     'bulk-job-css' => '73af99f5',
     'conduit-api-css' => 'ce2cfc41',
     'config-options-css' => '16c920ae',
     'conpherence-color-css' => 'b17746b0',
     'conpherence-durable-column-view' => '2d57072b',
     'conpherence-header-pane-css' => 'c9a3db8e',
     'conpherence-menu-css' => '67f4680d',
     'conpherence-message-pane-css' => 'd244db1e',
     'conpherence-notification-css' => '6a3d4e58',
     'conpherence-participant-pane-css' => '69e0058a',
     'conpherence-thread-manager' => 'aec8e38c',
     'conpherence-transaction-css' => '3a3f5e7e',
     'd3' => '9d068042',
     'diff-tree-view-css' => 'e2d3e222',
     'differential-changeset-view-css' => '60c3d405',
     'differential-core-view-css' => '7300a73e',
     'differential-revision-add-comment-css' => '7e5900d9',
     'differential-revision-comment-css' => '7dbc8d1d',
     'differential-revision-history-css' => '237a2979',
     'differential-revision-list-css' => '93d2df7d',
     'differential-table-of-contents-css' => 'bba788b9',
     'diffusion-css' => 'e46232d6',
     'diffusion-icons-css' => '23b31a1b',
     'diffusion-readme-css' => 'b68a76e4',
     'diffusion-repository-css' => 'b89e8c6c',
     'diviner-shared-css' => '4bd263b0',
     'font-fontawesome' => '3883938a',
     'font-lato' => '23631304',
     'fuel-grid-css' => '66697240',
     'fuel-handle-list-css' => '2c4cbeca',
     'fuel-map-css' => 'd6e31510',
     'fuel-menu-css' => '21f5d199',
     'global-drag-and-drop-css' => '1d2713a4',
     'harbormaster-css' => '8dfe16b2',
     'herald-css' => '648d39e2',
     'herald-rule-editor' => '2633bef7',
     'herald-test-css' => '7e7bbdae',
     'inline-comment-summary-css' => '81eb368d',
     'javelin-aphlict' => '022516b4',
     'javelin-behavior' => '1b6acc2a',
     'javelin-behavior-aphlict-dropdown' => 'e9a2940f',
     'javelin-behavior-aphlict-listen' => '4e61fa88',
     'javelin-behavior-aphlict-status' => 'c3703a16',
     'javelin-behavior-aphront-basic-tokenizer' => '3b4899b0',
-    'javelin-behavior-aphront-drag-and-drop-textarea' => '7df68a45',
+    'javelin-behavior-aphront-drag-and-drop-textarea' => '3277c62d',
     'javelin-behavior-aphront-form-disable-on-submit' => '55d7b788',
     'javelin-behavior-aphront-more' => '506aa3f4',
     'javelin-behavior-audio-source' => '3dc5ad43',
     'javelin-behavior-audit-preview' => 'b7b73831',
     'javelin-behavior-badge-view' => '92cdd7b6',
     'javelin-behavior-bulk-editor' => 'aa6d2308',
     'javelin-behavior-bulk-job-reload' => '3829a3cf',
     'javelin-behavior-calendar-month-view' => '158c64e0',
     'javelin-behavior-choose-control' => '04f8a1e3',
     'javelin-behavior-comment-actions' => '4dffaeb2',
     'javelin-behavior-config-reorder-fields' => '2539f834',
     'javelin-behavior-conpherence-menu' => '8c2ed2bf',
     'javelin-behavior-conpherence-participant-pane' => '43ba89a2',
     'javelin-behavior-conpherence-pontificate' => '4ae58b5a',
     'javelin-behavior-conpherence-search' => '91befbcc',
     'javelin-behavior-countdown-timer' => '6a162524',
     'javelin-behavior-dark-console' => '457f4d16',
     'javelin-behavior-dashboard-async-panel' => '9c01e364',
     'javelin-behavior-dashboard-move-panels' => 'a2ab19be',
     'javelin-behavior-dashboard-query-panel-select' => '1e413dc9',
     'javelin-behavior-dashboard-tab-panel' => '0116d3e8',
     'javelin-behavior-day-view' => '727a5a61',
     'javelin-behavior-desktop-notifications-control' => '070679fe',
     'javelin-behavior-detect-timezone' => '78bc5d94',
     'javelin-behavior-device' => 'ac2b1e01',
     'javelin-behavior-differential-diff-radios' => '925fe8cd',
     'javelin-behavior-differential-populate' => 'b86ef6c2',
     'javelin-behavior-diffusion-commit-branches' => '4b671572',
     'javelin-behavior-diffusion-commit-graph' => 'ac10c917',
     'javelin-behavior-diffusion-locate-file' => '87428eb2',
     'javelin-behavior-diffusion-pull-lastmodified' => 'c715c123',
     'javelin-behavior-document-engine' => '243d6c22',
     'javelin-behavior-doorkeeper-tag' => '6a85bc5a',
     'javelin-behavior-drydock-live-operation-status' => '47a0728b',
     'javelin-behavior-durable-column' => 'fa6f30b2',
     'javelin-behavior-editengine-reorder-configs' => '4842f137',
     'javelin-behavior-editengine-reorder-fields' => '0ad8d31f',
     'javelin-behavior-event-all-day' => '0b1bc990',
     'javelin-behavior-fancy-datepicker' => '36821f8d',
     'javelin-behavior-global-drag-and-drop' => '1cab0e9a',
     'javelin-behavior-harbormaster-log' => 'b347a301',
     'javelin-behavior-herald-rule-editor' => '0922e81d',
     'javelin-behavior-high-security-warning' => 'dae2d55b',
     'javelin-behavior-history-install' => '6a1583a8',
     'javelin-behavior-icon-composer' => '38a6cedb',
     'javelin-behavior-launch-icon-composer' => 'a17b84f1',
     'javelin-behavior-lightbox-attachments' => '14c7ab36',
     'javelin-behavior-line-chart' => 'ad258e28',
     'javelin-behavior-linked-container' => '74446546',
     'javelin-behavior-maniphest-batch-selector' => '139ef688',
     'javelin-behavior-maniphest-list-editor' => 'c687e867',
     'javelin-behavior-owners-path-editor' => 'ff688a7a',
     'javelin-behavior-passphrase-credential-control' => '48fe33d0',
     'javelin-behavior-phabricator-autofocus' => '65bb0011',
     'javelin-behavior-phabricator-clipboard-copy' => 'cf32921f',
     'javelin-behavior-phabricator-gesture' => 'b58d1a2a',
     'javelin-behavior-phabricator-gesture-example' => '242dedd0',
     'javelin-behavior-phabricator-keyboard-pager' => '1325b731',
     'javelin-behavior-phabricator-keyboard-shortcuts' => '42c44e8b',
     'javelin-behavior-phabricator-line-linker' => '0d915ff5',
     'javelin-behavior-phabricator-notification-example' => '29819b75',
     'javelin-behavior-phabricator-object-selector' => '98ef467f',
     'javelin-behavior-phabricator-oncopy' => 'da8f5259',
     'javelin-behavior-phabricator-remarkup-assist' => '54262396',
     'javelin-behavior-phabricator-reveal-content' => 'b105a3a6',
     'javelin-behavior-phabricator-search-typeahead' => '1cb7d027',
     'javelin-behavior-phabricator-show-older-transactions' => '8b5c7d65',
     'javelin-behavior-phabricator-tooltips' => '73ecc1f8',
     'javelin-behavior-phabricator-transaction-comment-form' => '2bdadf1a',
     'javelin-behavior-phabricator-transaction-list' => '9cec214e',
     'javelin-behavior-phabricator-watch-anchor' => 'a77e2cbd',
     'javelin-behavior-pholio-mock-edit' => '3eed1f2b',
     'javelin-behavior-pholio-mock-view' => '5aa1544e',
     'javelin-behavior-phui-dropdown-menu' => '5cf0501a',
     'javelin-behavior-phui-file-upload' => 'e150bd50',
     'javelin-behavior-phui-hovercards' => '183738e6',
     'javelin-behavior-phui-selectable-list' => 'b26a41e4',
     'javelin-behavior-phui-submenu' => 'b5e9bff9',
     'javelin-behavior-phui-tab-group' => '242aa08b',
     'javelin-behavior-phui-timer-control' => 'f84bcbf4',
     'javelin-behavior-phuix-example' => 'c2c500a7',
     'javelin-behavior-policy-control' => '0eaa33a9',
     'javelin-behavior-policy-rule-editor' => '9347f172',
     'javelin-behavior-project-boards' => '58cb6a88',
     'javelin-behavior-project-create' => '34c53422',
     'javelin-behavior-quicksand-blacklist' => '5a6f6a06',
     'javelin-behavior-read-only-warning' => 'b9109f8f',
     'javelin-behavior-redirect' => '407ee861',
     'javelin-behavior-refresh-csrf' => '46116c01',
     'javelin-behavior-remarkup-load-image' => '202bfa3f',
     'javelin-behavior-remarkup-preview' => 'd8a86cfb',
     'javelin-behavior-reorder-applications' => 'aa371860',
     'javelin-behavior-reorder-columns' => '8ac32fd9',
     'javelin-behavior-reorder-profile-menu-items' => 'e5bdb730',
     'javelin-behavior-repository-crossreference' => '44d48cd1',
     'javelin-behavior-scrollbar' => '92388bae',
     'javelin-behavior-search-reorder-queries' => 'b86f297f',
     'javelin-behavior-select-content' => 'e8240b50',
     'javelin-behavior-select-on-click' => '66365ee2',
     'javelin-behavior-setup-check-https' => '01384686',
     'javelin-behavior-stripe-payment-form' => '02cb4398',
     'javelin-behavior-test-payment-form' => '4a7fb02b',
     'javelin-behavior-time-typeahead' => '5803b9e7',
     'javelin-behavior-toggle-class' => '32db8374',
     'javelin-behavior-toggle-widget' => '8f959ad0',
     'javelin-behavior-trigger-rule-editor' => '398fdf13',
     'javelin-behavior-typeahead-browse' => '70245195',
     'javelin-behavior-typeahead-search' => '7b139193',
     'javelin-behavior-user-menu' => '60cd9241',
     'javelin-behavior-view-placeholder' => 'a9942052',
     'javelin-behavior-workflow' => '9623adc1',
     'javelin-chart' => '52e3ff03',
     'javelin-chart-curtain-view' => '86954222',
     'javelin-chart-function-label' => '81de1dab',
     'javelin-color' => '78f811c9',
     'javelin-cookie' => '05d290ef',
     'javelin-diffusion-locate-file-source' => '94243d89',
     'javelin-dom' => 'e4c7622a',
     'javelin-dynval' => '202a2e85',
     'javelin-event' => 'c03f2fb4',
     'javelin-external-editor-link-engine' => '48a8641f',
     'javelin-fx' => '34450586',
     'javelin-history' => '030b4f7a',
     'javelin-install' => '5902260c',
     'javelin-json' => '541f81c3',
     'javelin-leader' => '0d2490ce',
     'javelin-magical-init' => '98e6504a',
     'javelin-mask' => '7c4d8998',
     'javelin-quicksand' => 'd3799cb4',
     'javelin-reactor' => '1c850a26',
     'javelin-reactor-dom' => '6cfa0008',
     'javelin-reactor-node-calmer' => '225bbb98',
     'javelin-reactornode' => '72960bc1',
     'javelin-request' => '84e6891f',
     'javelin-resource' => '20514cc2',
     'javelin-routable' => '6a18c42e',
     'javelin-router' => '32755edb',
     'javelin-scrollbar' => 'a43ae2ae',
     'javelin-sound' => 'd4cc2d2a',
     'javelin-stratcom' => '0889b835',
     'javelin-tokenizer' => '89a1ae3a',
     'javelin-typeahead' => 'a4356cde',
     'javelin-typeahead-composite-source' => '22ee68a5',
     'javelin-typeahead-normalizer' => 'a241536a',
     'javelin-typeahead-ondemand-source' => '23387297',
     'javelin-typeahead-preloaded-source' => '5a79f6c3',
     'javelin-typeahead-source' => '8badee71',
     'javelin-typeahead-static-source' => '80bff3af',
     'javelin-uri' => '2e255291',
     'javelin-util' => 'edb4d8c9',
     'javelin-vector' => 'e9c80beb',
     'javelin-view' => '289bf236',
     'javelin-view-html' => 'f8c4e135',
     'javelin-view-interpreter' => '876506b6',
     'javelin-view-renderer' => '9aae2b66',
     'javelin-view-visitor' => '308f9fe4',
     'javelin-websocket' => 'fdc13e4e',
     'javelin-workboard-board' => 'b46d88c5',
     'javelin-workboard-card' => '0392a5d8',
     'javelin-workboard-card-template' => '84f82dad',
     'javelin-workboard-column' => 'c3d24e63',
     'javelin-workboard-controller' => 'b9d0c2f3',
     'javelin-workboard-drop-effect' => '8e0aa661',
     'javelin-workboard-header' => '111bfd2d',
     'javelin-workboard-header-template' => 'ebe83a6b',
     'javelin-workboard-order-template' => '03e8891f',
     'javelin-workflow' => '945ff654',
     'maniphest-report-css' => '3d53188b',
     'maniphest-task-edit-css' => '272daa84',
     'maniphest-task-summary-css' => '61d1667e',
     'multirow-row-manager' => '5b54c823',
     'owners-path-editor' => '2a8b62d9',
     'owners-path-editor-css' => 'fa7c13ef',
     'paste-css' => 'b37bcd38',
     'path-typeahead' => 'ad486db3',
     'people-picture-menu-item-css' => 'fe8e07cf',
     'people-profile-css' => '2ea2daa1',
     'phabricator-action-list-view-css' => '1b0085b2',
     'phabricator-busy' => '5202e831',
     'phabricator-chatlog-css' => 'abdc76ee',
     'phabricator-content-source-view-css' => 'cdf0d579',
     'phabricator-core-css' => 'b3ebd90d',
     'phabricator-countdown-css' => 'bff8012f',
     'phabricator-darklog' => '3b869402',
     'phabricator-darkmessage' => '26cd4b73',
     'phabricator-dashboard-css' => '5a205b9d',
     'phabricator-diff-changeset' => 'd7d3ba75',
     'phabricator-diff-changeset-list' => 'cc2c5de5',
     'phabricator-diff-inline' => '9c775532',
     'phabricator-diff-inline-content-state' => 'aa51efb4',
     'phabricator-diff-path-view' => '8207abf9',
     'phabricator-diff-tree-view' => '5d83623b',
     'phabricator-drag-and-drop-file-upload' => '4370900d',
     'phabricator-draggable-list' => '0169e425',
     'phabricator-fatal-config-template-css' => '20babf50',
     'phabricator-favicon' => '7930776a',
     'phabricator-feed-css' => 'd8b6e3f8',
     'phabricator-file-upload' => 'ab85e184',
     'phabricator-flag-css' => '2b77be8d',
     'phabricator-keyboard-shortcut' => '1a844c06',
     'phabricator-keyboard-shortcut-manager' => '81debc48',
     'phabricator-main-menu-view' => 'bcec20f0',
     'phabricator-nav-view-css' => '423f92cc',
     'phabricator-notification' => 'a9b91e3f',
     'phabricator-notification-css' => '30240bd2',
     'phabricator-notification-menu-css' => '4df1ee30',
     'phabricator-object-selector-css' => 'ee77366f',
     'phabricator-phtize' => '2f1db1ed',
     'phabricator-prefab' => '5793d835',
     'phabricator-remarkup-css' => '5baa3bd9',
     'phabricator-search-results-css' => '9ea70ace',
     'phabricator-shaped-request' => '995f5102',
     'phabricator-slowvote-css' => '1694baed',
     'phabricator-source-code-view-css' => '03d7ac28',
     'phabricator-standard-page-view' => 'a374f94c',
     'phabricator-textareautils' => 'f340a484',
     'phabricator-title' => '43bc9360',
     'phabricator-tooltip' => '83754533',
     'phabricator-ui-example-css' => 'b4795059',
     'phabricator-zindex-css' => 'ac3bfcd4',
     'phame-css' => 'bb442327',
     'pholio-css' => '88ef5ef1',
     'pholio-edit-css' => '4df55b3b',
     'pholio-inline-comments-css' => '722b48c2',
     'phortune-credit-card-form' => 'd12d214f',
     'phortune-credit-card-form-css' => '3b9868a8',
     'phortune-css' => '508a1a5e',
     'phortune-invoice-css' => '4436b241',
     'phrequent-css' => 'bd79cc67',
     'phriction-document-css' => '03380da0',
     'phui-action-panel-css' => '6c386cbf',
     'phui-badge-view-css' => '666e25ad',
     'phui-basic-nav-view-css' => '56ebd66d',
     'phui-big-info-view-css' => '362ad37b',
     'phui-box-css' => '5ed3b8cb',
     'phui-bulk-editor-css' => '374d5e30',
     'phui-button-bar-css' => 'a4aa75c4',
     'phui-button-css' => 'ea704902',
     'phui-button-simple-css' => '1ff278aa',
     'phui-calendar-css' => 'f11073aa',
     'phui-calendar-day-css' => '9597d706',
     'phui-calendar-list-css' => 'ccd7e4e2',
     'phui-calendar-month-css' => 'cb758c42',
     'phui-chart-css' => '14df9ae3',
     'phui-cms-css' => '8c05c41e',
     'phui-comment-form-css' => '68a2d99a',
     'phui-comment-panel-css' => 'ec4e31c0',
     'phui-crumbs-view-css' => '614f43cf',
     'phui-curtain-object-ref-view-css' => '5f752bdb',
     'phui-curtain-view-css' => '68c5efb6',
     'phui-document-summary-view-css' => 'b068eed1',
     'phui-document-view-css' => '52b748a5',
     'phui-document-view-pro-css' => 'b9613a10',
     'phui-feed-story-css' => 'a0c05029',
     'phui-font-icon-base-css' => '303c9b87',
     'phui-fontkit-css' => '1ec937e5',
     'phui-form-css' => '1f177cb7',
     'phui-form-view-css' => '01b796c0',
     'phui-formation-view-css' => 'd2dec8ed',
     'phui-head-thing-view-css' => 'd7f293df',
     'phui-header-view-css' => '36c86a58',
     'phui-hovercard' => '6199f752',
     'phui-hovercard-list' => 'de4b4919',
     'phui-hovercard-view-css' => '6ca90fa0',
     'phui-icon-set-selector-css' => '7aa5f3ec',
     'phui-icon-view-css' => '4cbc684a',
     'phui-image-mask-css' => '62c7f4d2',
     'phui-info-view-css' => 'a10a909b',
     'phui-inline-comment-view-css' => '9863a85e',
     'phui-invisible-character-view-css' => 'c694c4a4',
     'phui-left-right-css' => '68513c34',
     'phui-lightbox-css' => '4ebf22da',
     'phui-list-view-css' => '0c04affd',
     'phui-object-box-css' => 'b8d7eea0',
     'phui-oi-big-ui-css' => 'fa74cc35',
     'phui-oi-color-css' => 'b517bfa0',
     'phui-oi-drag-ui-css' => 'da15d3dc',
     'phui-oi-flush-ui-css' => '490e2e2e',
     'phui-oi-list-view-css' => 'af98a277',
     'phui-oi-simple-ui-css' => '6a30fa46',
     'phui-pager-css' => 'd022c7ad',
     'phui-pinboard-view-css' => '1f08f5d8',
     'phui-policy-section-view-css' => '139fdc64',
     'phui-property-list-view-css' => '5adf7078',
     'phui-remarkup-preview-css' => '91767007',
     'phui-segment-bar-view-css' => '5166b370',
     'phui-spacing-css' => 'b05cadc3',
     'phui-status-list-view-css' => '293b5dad',
     'phui-tag-view-css' => 'fb811341',
     'phui-theme-css' => '35883b37',
     'phui-timeline-view-css' => '2d32d7a9',
     'phui-two-column-view-css' => 'f96d319f',
     'phui-workboard-color-css' => 'e86de308',
     'phui-workboard-view-css' => '74fc9d98',
     'phui-workcard-view-css' => '913441b6',
     'phui-workpanel-view-css' => '3ae89b20',
     'phuix-action-list-view' => 'c68f183f',
     'phuix-action-view' => 'a8f573a9',
     'phuix-autocomplete' => '2fbe234d',
     'phuix-button-view' => '55a24e84',
     'phuix-dropdown-menu' => 'b557770a',
     'phuix-form-control-view' => '38c1f3fb',
     'phuix-formation-column-view' => '4bcc1f78',
     'phuix-formation-flank-view' => '6648270a',
     'phuix-formation-view' => 'cef53b3e',
     'phuix-icon-view' => 'a5257c4e',
     'policy-css' => 'ceb56a08',
     'policy-edit-css' => '8794e2ed',
     'policy-transaction-detail-css' => 'c02b8384',
     'ponder-view-css' => '05a09d0a',
     'project-card-view-css' => 'a9f2c2dd',
     'project-triggers-css' => 'cd9c8bb9',
     'project-view-css' => '567858b3',
     'setup-issue-css' => '5eed85b2',
     'sprite-login-css' => '18b368a6',
     'sprite-tokens-css' => 'f1896dc5',
     'syntax-default-css' => '055fc231',
     'syntax-highlighting-css' => '548567f6',
     'tokens-css' => 'ce5a50bd',
     'trigger-rule' => '41b7b4f6',
     'trigger-rule-control' => '5faf27b9',
     'trigger-rule-editor' => 'b49fd60c',
     'trigger-rule-type' => '4feea7d3',
     'typeahead-browse-css' => 'b7ed02d2',
     'unhandled-exception-css' => '9ecfc00d',
   ),
   'requires' => array(
     '0116d3e8' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-stratcom',
     ),
     '01384686' => array(
       'javelin-behavior',
       'javelin-uri',
       'phabricator-notification',
     ),
     '0169e425' => array(
       'javelin-install',
       'javelin-dom',
       'javelin-stratcom',
       'javelin-util',
       'javelin-vector',
       'javelin-magical-init',
     ),
     '022516b4' => array(
       'javelin-install',
       'javelin-util',
       'javelin-websocket',
       'javelin-leader',
       'javelin-json',
     ),
     '02cb4398' => array(
       'javelin-behavior',
       'javelin-dom',
       'phortune-credit-card-form',
     ),
     '030b4f7a' => array(
       'javelin-stratcom',
       'javelin-install',
       'javelin-uri',
       'javelin-util',
     ),
     '0392a5d8' => array(
       'javelin-install',
     ),
     '03e8891f' => array(
       'javelin-install',
     ),
     '04f8a1e3' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
       'javelin-workflow',
     ),
     '05d290ef' => array(
       'javelin-install',
       'javelin-util',
     ),
     '070679fe' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
       'javelin-uri',
       'phabricator-notification',
     ),
     '0889b835' => array(
       'javelin-install',
       'javelin-event',
       'javelin-util',
       'javelin-magical-init',
     ),
     '0922e81d' => array(
       'herald-rule-editor',
       'javelin-behavior',
     ),
     '0ad8d31f' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-workflow',
       'javelin-dom',
       'phabricator-draggable-list',
     ),
     '0d2490ce' => array(
       'javelin-install',
     ),
     '0d915ff5' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
       'javelin-history',
       'javelin-external-editor-link-engine',
     ),
     '0eaa33a9' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-util',
       'phuix-dropdown-menu',
       'phuix-action-list-view',
       'phuix-action-view',
       'javelin-workflow',
       'phuix-icon-view',
     ),
     '111bfd2d' => array(
       'javelin-install',
     ),
     '1325b731' => array(
       'javelin-behavior',
       'javelin-uri',
       'phabricator-keyboard-shortcut',
     ),
     '139ef688' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-stratcom',
       'javelin-util',
     ),
     '14c7ab36' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
       'javelin-mask',
       'javelin-util',
       'phuix-icon-view',
       'phabricator-busy',
     ),
     '183738e6' => array(
       'javelin-behavior',
       'javelin-behavior-device',
       'javelin-stratcom',
       'javelin-vector',
       'phui-hovercard',
       'phui-hovercard-list',
     ),
     '1a844c06' => array(
       'javelin-install',
       'javelin-util',
       'phabricator-keyboard-shortcut-manager',
     ),
     '1b6acc2a' => array(
       'javelin-magical-init',
       'javelin-util',
     ),
     '1c850a26' => array(
       'javelin-install',
       'javelin-util',
     ),
     '1cab0e9a' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-uri',
       'javelin-mask',
       'phabricator-drag-and-drop-file-upload',
     ),
     '1cb7d027' => array(
       'javelin-behavior',
       'javelin-typeahead-ondemand-source',
       'javelin-typeahead',
       'javelin-dom',
       'javelin-uri',
       'javelin-util',
       'javelin-stratcom',
       'phabricator-prefab',
       'phuix-icon-view',
     ),
     '1e413dc9' => array(
       'javelin-behavior',
       'javelin-dom',
     ),
     '1ff278aa' => array(
       'phui-button-css',
     ),
     '202a2e85' => array(
       'javelin-install',
       'javelin-reactornode',
       'javelin-util',
       'javelin-reactor',
     ),
     '202bfa3f' => array(
       'javelin-behavior',
       'javelin-request',
     ),
     '20514cc2' => array(
       'javelin-util',
       'javelin-uri',
       'javelin-install',
     ),
     '225bbb98' => array(
       'javelin-install',
       'javelin-reactor',
       'javelin-util',
     ),
     '22ee68a5' => array(
       'javelin-install',
       'javelin-typeahead-source',
       'javelin-util',
     ),
     23387297 => array(
       'javelin-install',
       'javelin-util',
       'javelin-request',
       'javelin-typeahead-source',
     ),
     23631304 => array(
       'phui-fontkit-css',
     ),
     '242aa08b' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
     ),
     '242dedd0' => array(
       'javelin-stratcom',
       'javelin-behavior',
       'javelin-vector',
       'javelin-dom',
     ),
     '243d6c22' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-stratcom',
     ),
     '2539f834' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
       'javelin-json',
       'phabricator-draggable-list',
     ),
     '2633bef7' => array(
       'multirow-row-manager',
       'javelin-install',
       'javelin-util',
       'javelin-dom',
       'javelin-stratcom',
       'javelin-json',
       'phabricator-prefab',
     ),
     '289bf236' => array(
       'javelin-install',
       'javelin-util',
     ),
     '29819b75' => array(
       'phabricator-notification',
       'javelin-stratcom',
       'javelin-behavior',
     ),
     '2a8b62d9' => array(
       'multirow-row-manager',
       'javelin-install',
       'path-typeahead',
       'javelin-dom',
       'javelin-util',
       'phabricator-prefab',
       'phuix-form-control-view',
     ),
     '2bdadf1a' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-util',
       'javelin-request',
       'phabricator-shaped-request',
     ),
     '2e255291' => array(
       'javelin-install',
       'javelin-util',
       'javelin-stratcom',
     ),
     '2f1db1ed' => array(
       'javelin-util',
     ),
     '2fbe234d' => array(
       'javelin-install',
       'javelin-dom',
       'phuix-icon-view',
       'phabricator-prefab',
     ),
     '308f9fe4' => array(
       'javelin-install',
       'javelin-util',
     ),
     '32755edb' => array(
       'javelin-install',
       'javelin-util',
     ),
+    '3277c62d' => array(
+      'javelin-behavior',
+      'javelin-dom',
+      'javelin-json',
+      'phabricator-drag-and-drop-file-upload',
+      'phabricator-textareautils',
+    ),
     '32db8374' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
     ),
     34450586 => array(
       'javelin-color',
       'javelin-install',
       'javelin-util',
     ),
     '34c53422' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-stratcom',
       'javelin-workflow',
     ),
     '34e2a838' => array(
       'aphront-typeahead-control-css',
       'phui-tag-view-css',
     ),
     '36821f8d' => array(
       'javelin-behavior',
       'javelin-util',
       'javelin-dom',
       'javelin-stratcom',
       'javelin-vector',
     ),
     '3829a3cf' => array(
       'javelin-behavior',
       'javelin-uri',
     ),
     '38a6cedb' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-stratcom',
     ),
     '38c1f3fb' => array(
       'javelin-install',
       'javelin-dom',
     ),
     '398fdf13' => array(
       'javelin-behavior',
       'trigger-rule-editor',
       'trigger-rule',
       'trigger-rule-type',
     ),
     '3ae89b20' => array(
       'phui-workcard-view-css',
     ),
     '3b4899b0' => array(
       'javelin-behavior',
       'phabricator-prefab',
     ),
     '3dc5ad43' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-vector',
       'javelin-dom',
     ),
     '3eed1f2b' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
       'javelin-workflow',
       'javelin-quicksand',
       'phabricator-phtize',
       'phabricator-drag-and-drop-file-upload',
       'phabricator-draggable-list',
     ),
     '407ee861' => array(
       'javelin-behavior',
       'javelin-uri',
     ),
     '42c44e8b' => array(
       'javelin-behavior',
       'javelin-workflow',
       'javelin-json',
       'javelin-dom',
       'phabricator-keyboard-shortcut',
     ),
     '4370900d' => array(
       'javelin-install',
       'javelin-util',
       'javelin-request',
       'javelin-dom',
       'javelin-uri',
       'phabricator-file-upload',
     ),
     '43ba89a2' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-stratcom',
       'javelin-workflow',
       'javelin-util',
       'phabricator-notification',
       'conpherence-thread-manager',
     ),
     '43bc9360' => array(
       'javelin-install',
     ),
     '44d48cd1' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-stratcom',
       'javelin-uri',
     ),
     '457f4d16' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-util',
       'javelin-dom',
       'javelin-request',
       'phabricator-keyboard-shortcut',
       'phabricator-darklog',
       'phabricator-darkmessage',
     ),
     '46116c01' => array(
       'javelin-request',
       'javelin-behavior',
       'javelin-dom',
       'javelin-router',
       'javelin-util',
       'phabricator-busy',
     ),
     '47a0728b' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-request',
     ),
     '4842f137' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-workflow',
       'javelin-dom',
       'phabricator-draggable-list',
     ),
     '48a8641f' => array(
       'javelin-install',
     ),
     '48fe33d0' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-stratcom',
       'javelin-workflow',
       'javelin-util',
       'javelin-uri',
     ),
     '490e2e2e' => array(
       'phui-oi-list-view-css',
     ),
     '4a7fb02b' => array(
       'javelin-behavior',
       'javelin-dom',
       'phortune-credit-card-form',
     ),
     '4ae58b5a' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-util',
       'javelin-workflow',
       'javelin-stratcom',
       'conpherence-thread-manager',
     ),
     '4b671572' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-util',
       'javelin-request',
     ),
     '4bcc1f78' => array(
       'javelin-install',
       'javelin-dom',
     ),
     '4dffaeb2' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-workflow',
       'javelin-dom',
       'phuix-form-control-view',
       'phuix-icon-view',
       'javelin-behavior-phabricator-gesture',
     ),
     '4e61fa88' => array(
       'javelin-behavior',
       'javelin-aphlict',
       'javelin-stratcom',
       'javelin-request',
       'javelin-uri',
       'javelin-dom',
       'javelin-json',
       'javelin-router',
       'javelin-util',
       'javelin-leader',
       'javelin-sound',
       'phabricator-notification',
     ),
     '4feea7d3' => array(
       'trigger-rule-control',
     ),
     '506aa3f4' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
     ),
     '5202e831' => array(
       'javelin-install',
       'javelin-dom',
       'javelin-fx',
     ),
     '52e3ff03' => array(
       'phui-chart-css',
       'd3',
       'javelin-chart-curtain-view',
       'javelin-chart-function-label',
     ),
     '541f81c3' => array(
       'javelin-install',
     ),
     54262396 => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
       'phabricator-phtize',
       'phabricator-textareautils',
       'javelin-workflow',
       'javelin-vector',
       'phuix-autocomplete',
       'javelin-mask',
     ),
     '548567f6' => array(
       'syntax-default-css',
     ),
     '55a24e84' => array(
       'javelin-install',
       'javelin-dom',
     ),
     '55d7b788' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
     ),
     '5793d835' => array(
       'javelin-install',
       'javelin-util',
       'javelin-dom',
       'javelin-typeahead',
       'javelin-tokenizer',
       'javelin-typeahead-preloaded-source',
       'javelin-typeahead-ondemand-source',
       'javelin-dom',
       'javelin-stratcom',
       'javelin-util',
     ),
     '5803b9e7' => array(
       'javelin-behavior',
       'javelin-util',
       'javelin-dom',
       'javelin-stratcom',
       'javelin-vector',
       'javelin-typeahead-static-source',
     ),
     '58cb6a88' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-util',
       'javelin-vector',
       'javelin-stratcom',
       'javelin-workflow',
       'javelin-workboard-controller',
       'javelin-workboard-drop-effect',
     ),
     '5902260c' => array(
       'javelin-util',
       'javelin-magical-init',
     ),
     '5a6f6a06' => array(
       'javelin-behavior',
       'javelin-quicksand',
     ),
     '5a79f6c3' => array(
       'javelin-install',
       'javelin-util',
       'javelin-request',
       'javelin-typeahead-source',
     ),
     '5aa1544e' => array(
       'javelin-behavior',
       'javelin-util',
       'javelin-stratcom',
       'javelin-dom',
       'javelin-vector',
       'javelin-magical-init',
       'javelin-request',
       'javelin-history',
       'javelin-workflow',
       'javelin-mask',
       'javelin-behavior-device',
       'phabricator-keyboard-shortcut',
     ),
     '5b54c823' => array(
       'javelin-install',
       'javelin-stratcom',
       'javelin-dom',
       'javelin-util',
     ),
     '5cf0501a' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
       'phuix-dropdown-menu',
     ),
     '5d83623b' => array(
       'javelin-dom',
     ),
     '5faf27b9' => array(
       'phuix-form-control-view',
     ),
     '60c3d405' => array(
       'phui-inline-comment-view-css',
     ),
     '60cd9241' => array(
       'javelin-behavior',
     ),
     '6199f752' => array(
       'javelin-install',
       'javelin-dom',
       'javelin-vector',
       'javelin-request',
       'javelin-uri',
     ),
     '65bb0011' => array(
       'javelin-behavior',
       'javelin-dom',
     ),
     '66365ee2' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
     ),
     '6648270a' => array(
       'javelin-install',
       'javelin-dom',
     ),
     '6a1583a8' => array(
       'javelin-behavior',
       'javelin-history',
     ),
     '6a162524' => array(
       'javelin-behavior',
       'javelin-dom',
     ),
     '6a18c42e' => array(
       'javelin-install',
     ),
     '6a30fa46' => array(
       'phui-oi-list-view-css',
     ),
     '6a85bc5a' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-json',
       'javelin-workflow',
       'javelin-magical-init',
     ),
     '6cfa0008' => array(
       'javelin-dom',
       'javelin-dynval',
       'javelin-reactor',
       'javelin-reactornode',
       'javelin-install',
       'javelin-util',
     ),
     70245195 => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-workflow',
       'javelin-dom',
     ),
     '727a5a61' => array(
       'phuix-icon-view',
     ),
     '72960bc1' => array(
       'javelin-install',
       'javelin-reactor',
       'javelin-util',
       'javelin-reactor-node-calmer',
     ),
     '73ecc1f8' => array(
       'javelin-behavior',
       'javelin-behavior-device',
       'javelin-stratcom',
       'phabricator-tooltip',
     ),
     74446546 => array(
       'javelin-behavior',
       'javelin-dom',
     ),
     '78bc5d94' => array(
       'javelin-behavior',
       'javelin-uri',
       'phabricator-notification',
     ),
     '78f811c9' => array(
       'javelin-install',
     ),
     '7930776a' => array(
       'javelin-install',
       'javelin-dom',
     ),
     '7b139193' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-workflow',
       'javelin-dom',
     ),
     '7c4d8998' => array(
       'javelin-install',
       'javelin-dom',
     ),
-    '7df68a45' => array(
-      'javelin-behavior',
-      'javelin-dom',
-      'javelin-json',
-      'phabricator-drag-and-drop-file-upload',
-      'phabricator-textareautils',
-    ),
     '80bff3af' => array(
       'javelin-install',
       'javelin-typeahead-source',
     ),
     '81debc48' => array(
       'javelin-install',
       'javelin-util',
       'javelin-stratcom',
       'javelin-dom',
       'javelin-vector',
     ),
     '8207abf9' => array(
       'javelin-dom',
     ),
     83754533 => array(
       'javelin-install',
       'javelin-util',
       'javelin-dom',
       'javelin-vector',
     ),
     '84e6891f' => array(
       'javelin-install',
       'javelin-stratcom',
       'javelin-util',
       'javelin-behavior',
       'javelin-json',
       'javelin-dom',
       'javelin-resource',
       'javelin-routable',
     ),
     '84f82dad' => array(
       'javelin-install',
     ),
     '87428eb2' => array(
       'javelin-behavior',
       'javelin-diffusion-locate-file-source',
       'javelin-dom',
       'javelin-typeahead',
       'javelin-uri',
     ),
     '876506b6' => array(
       'javelin-view',
       'javelin-install',
       'javelin-dom',
     ),
     '89a1ae3a' => array(
       'javelin-dom',
       'javelin-util',
       'javelin-stratcom',
       'javelin-install',
     ),
     '8ac32fd9' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-workflow',
       'javelin-dom',
       'phabricator-draggable-list',
     ),
     '8b5c7d65' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
       'phabricator-busy',
     ),
     '8badee71' => array(
       'javelin-install',
       'javelin-util',
       'javelin-dom',
       'javelin-typeahead-normalizer',
     ),
     '8c2ed2bf' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-util',
       'javelin-stratcom',
       'javelin-workflow',
       'javelin-behavior-device',
       'javelin-history',
       'javelin-vector',
       'javelin-scrollbar',
       'phabricator-title',
       'phabricator-shaped-request',
       'conpherence-thread-manager',
     ),
     '8e0aa661' => array(
       'javelin-install',
       'javelin-dom',
     ),
     '8f959ad0' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-util',
       'javelin-workflow',
       'javelin-stratcom',
     ),
     '91befbcc' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-util',
       'javelin-workflow',
       'javelin-stratcom',
     ),
     '92388bae' => array(
       'javelin-behavior',
       'javelin-scrollbar',
     ),
     '925fe8cd' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
     ),
     '92cdd7b6' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
     ),
     '9347f172' => array(
       'javelin-behavior',
       'multirow-row-manager',
       'javelin-dom',
       'javelin-util',
       'phabricator-prefab',
       'javelin-json',
     ),
     '94243d89' => array(
       'javelin-install',
       'javelin-dom',
       'javelin-typeahead-preloaded-source',
       'javelin-util',
     ),
     '945ff654' => array(
       'javelin-stratcom',
       'javelin-request',
       'javelin-dom',
       'javelin-vector',
       'javelin-install',
       'javelin-util',
       'javelin-mask',
       'javelin-uri',
       'javelin-routable',
     ),
     '9623adc1' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-workflow',
       'javelin-dom',
       'javelin-router',
     ),
     '98ef467f' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-request',
       'javelin-util',
     ),
     '995f5102' => array(
       'javelin-install',
       'javelin-util',
       'javelin-request',
       'javelin-router',
     ),
     '9aae2b66' => array(
       'javelin-install',
       'javelin-util',
     ),
     '9c01e364' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-workflow',
     ),
     '9c775532' => array(
       'javelin-dom',
       'phabricator-diff-inline-content-state',
     ),
     '9cec214e' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-workflow',
       'javelin-dom',
       'javelin-uri',
       'phabricator-textareautils',
     ),
     'a17b84f1' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-workflow',
     ),
     'a241536a' => array(
       'javelin-install',
     ),
     'a2ab19be' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-util',
       'javelin-stratcom',
       'javelin-workflow',
       'phabricator-draggable-list',
     ),
     'a4356cde' => array(
       'javelin-install',
       'javelin-dom',
       'javelin-vector',
       'javelin-util',
     ),
     'a43ae2ae' => array(
       'javelin-install',
       'javelin-dom',
       'javelin-stratcom',
       'javelin-vector',
     ),
     'a4aa75c4' => array(
       'phui-button-css',
       'phui-button-simple-css',
     ),
     'a5257c4e' => array(
       'javelin-install',
       'javelin-dom',
     ),
     'a77e2cbd' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
       'javelin-vector',
     ),
     'a8f573a9' => array(
       'javelin-install',
       'javelin-dom',
       'javelin-util',
     ),
     'a9942052' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-view-renderer',
       'javelin-install',
     ),
     'a9b91e3f' => array(
       'javelin-install',
       'javelin-dom',
       'javelin-stratcom',
       'javelin-util',
       'phabricator-notification-css',
     ),
     'aa371860' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-workflow',
       'javelin-dom',
       'phabricator-draggable-list',
     ),
     'aa51efb4' => array(
       'javelin-dom',
     ),
     'aa6d2308' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-util',
       'multirow-row-manager',
       'javelin-json',
       'phuix-form-control-view',
     ),
     'ab85e184' => array(
       'javelin-install',
       'javelin-dom',
       'phabricator-notification',
     ),
     'ac10c917' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-stratcom',
     ),
     'ac2b1e01' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
       'javelin-vector',
       'javelin-install',
     ),
     'ad258e28' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-chart',
     ),
     'ad486db3' => array(
       'javelin-install',
       'javelin-typeahead',
       'javelin-dom',
       'javelin-request',
       'javelin-typeahead-ondemand-source',
       'javelin-util',
     ),
     'aec8e38c' => array(
       'javelin-dom',
       'javelin-util',
       'javelin-stratcom',
       'javelin-install',
       'javelin-aphlict',
       'javelin-workflow',
       'javelin-router',
       'javelin-behavior-device',
       'javelin-vector',
     ),
     'b105a3a6' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
     ),
     'b26a41e4' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
     ),
     'b347a301' => array(
       'javelin-behavior',
     ),
     'b46d88c5' => array(
       'javelin-install',
       'javelin-dom',
       'javelin-util',
       'javelin-stratcom',
       'javelin-workflow',
       'phabricator-draggable-list',
       'javelin-workboard-column',
       'javelin-workboard-header-template',
       'javelin-workboard-card-template',
       'javelin-workboard-order-template',
     ),
     'b49fd60c' => array(
       'multirow-row-manager',
       'trigger-rule',
     ),
     'b517bfa0' => array(
       'phui-oi-list-view-css',
     ),
     'b557770a' => array(
       'javelin-install',
       'javelin-util',
       'javelin-dom',
       'javelin-vector',
       'javelin-stratcom',
     ),
     'b58d1a2a' => array(
       'javelin-behavior',
       'javelin-behavior-device',
       'javelin-stratcom',
       'javelin-vector',
       'javelin-dom',
       'javelin-magical-init',
     ),
     'b5e9bff9' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
     ),
     'b7b73831' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-util',
       'phabricator-shaped-request',
     ),
     'b86ef6c2' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-stratcom',
       'phabricator-tooltip',
       'phabricator-diff-changeset-list',
       'phabricator-diff-changeset',
       'phuix-formation-view',
     ),
     'b86f297f' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-workflow',
       'javelin-dom',
       'phabricator-draggable-list',
     ),
     'b9109f8f' => array(
       'javelin-behavior',
       'javelin-uri',
       'phabricator-notification',
     ),
     'b9d0c2f3' => array(
       'javelin-install',
       'javelin-dom',
       'javelin-util',
       'javelin-vector',
       'javelin-stratcom',
       'javelin-workflow',
       'phabricator-drag-and-drop-file-upload',
       'javelin-workboard-board',
     ),
     'bcec20f0' => array(
       'phui-theme-css',
     ),
     'c03f2fb4' => array(
       'javelin-install',
     ),
     'c2c500a7' => array(
       'javelin-install',
       'javelin-dom',
       'phuix-button-view',
     ),
     'c3703a16' => array(
       'javelin-behavior',
       'javelin-aphlict',
       'phabricator-phtize',
       'javelin-dom',
     ),
     'c3d24e63' => array(
       'javelin-install',
       'javelin-workboard-card',
       'javelin-workboard-header',
     ),
     'c687e867' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-stratcom',
       'javelin-workflow',
       'javelin-fx',
       'javelin-util',
     ),
     'c68f183f' => array(
       'javelin-install',
       'javelin-dom',
     ),
     'c715c123' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-util',
       'javelin-workflow',
       'javelin-json',
     ),
     'cc2c5de5' => array(
       'javelin-install',
       'phuix-button-view',
       'phabricator-diff-tree-view',
     ),
     'cef53b3e' => array(
       'javelin-install',
       'javelin-dom',
       'phuix-formation-column-view',
       'phuix-formation-flank-view',
     ),
     'cf32921f' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-stratcom',
     ),
     'd12d214f' => array(
       'javelin-install',
       'javelin-dom',
       'javelin-json',
       'javelin-workflow',
       'javelin-util',
     ),
     'd3799cb4' => array(
       'javelin-install',
     ),
     'd4cc2d2a' => array(
       'javelin-install',
     ),
     'd7d3ba75' => array(
       'javelin-dom',
       'javelin-util',
       'javelin-stratcom',
       'javelin-install',
       'javelin-workflow',
       'javelin-router',
       'javelin-behavior-device',
       'javelin-vector',
       'phabricator-diff-inline',
       'phabricator-diff-path-view',
       'phuix-button-view',
       'javelin-external-editor-link-engine',
     ),
     'd8a86cfb' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-util',
       'phabricator-shaped-request',
     ),
     'da15d3dc' => array(
       'phui-oi-list-view-css',
     ),
     'da8f5259' => array(
       'javelin-behavior',
       'javelin-dom',
     ),
     'dae2d55b' => array(
       'javelin-behavior',
       'javelin-uri',
       'phabricator-notification',
     ),
     'de4b4919' => array(
       'javelin-install',
       'javelin-dom',
       'javelin-vector',
       'javelin-request',
       'javelin-uri',
       'phui-hovercard',
     ),
     'e150bd50' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
       'phuix-dropdown-menu',
     ),
     'e4c7622a' => array(
       'javelin-magical-init',
       'javelin-install',
       'javelin-util',
       'javelin-vector',
       'javelin-stratcom',
     ),
     'e5bdb730' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-workflow',
       'javelin-dom',
       'phabricator-draggable-list',
     ),
     'e8240b50' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
     ),
     'e9a2940f' => array(
       'javelin-behavior',
       'javelin-request',
       'javelin-stratcom',
       'javelin-vector',
       'javelin-dom',
       'javelin-uri',
       'javelin-behavior-device',
       'phabricator-title',
       'phabricator-favicon',
     ),
     'e9c80beb' => array(
       'javelin-install',
       'javelin-event',
     ),
     'ebe83a6b' => array(
       'javelin-install',
     ),
     'ec4e31c0' => array(
       'phui-timeline-view-css',
     ),
     'ee77366f' => array(
       'aphront-dialog-view-css',
     ),
     'f340a484' => array(
       'javelin-install',
       'javelin-dom',
       'javelin-vector',
     ),
     'f84bcbf4' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
     ),
     'f8c4e135' => array(
       'javelin-install',
       'javelin-dom',
       'javelin-view-visitor',
       'javelin-util',
     ),
     'fa6f30b2' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-stratcom',
       'javelin-behavior-device',
       'javelin-scrollbar',
       'javelin-quicksand',
       'phabricator-keyboard-shortcut',
       'conpherence-thread-manager',
     ),
     'fa74cc35' => array(
       'phui-oi-list-view-css',
     ),
     'fdc13e4e' => array(
       'javelin-install',
     ),
     'ff688a7a' => array(
       'owners-path-editor',
       'javelin-behavior',
     ),
   ),
   'packages' => array(
     'conpherence.pkg.css' => array(
       'conpherence-menu-css',
       'conpherence-color-css',
       'conpherence-message-pane-css',
       'conpherence-notification-css',
       'conpherence-transaction-css',
       'conpherence-participant-pane-css',
       'conpherence-header-pane-css',
     ),
     'conpherence.pkg.js' => array(
       'javelin-behavior-conpherence-menu',
       'javelin-behavior-conpherence-participant-pane',
       'javelin-behavior-conpherence-pontificate',
       'javelin-behavior-toggle-widget',
     ),
     'core.pkg.css' => array(
       'phabricator-core-css',
       'phabricator-zindex-css',
       'phui-button-css',
       'phui-button-simple-css',
       'phui-theme-css',
       'phabricator-standard-page-view',
       'aphront-dialog-view-css',
       'phui-form-view-css',
       'aphront-panel-view-css',
       'aphront-table-view-css',
       'aphront-tokenizer-control-css',
       'aphront-typeahead-control-css',
       'aphront-list-filter-view-css',
       'application-search-view-css',
       'phabricator-remarkup-css',
       'syntax-highlighting-css',
       'syntax-default-css',
       'phui-pager-css',
       'aphront-tooltip-css',
       'phabricator-flag-css',
       'phui-info-view-css',
       'phabricator-main-menu-view',
       'phabricator-notification-css',
       'phabricator-notification-menu-css',
       'phui-lightbox-css',
       'phui-comment-panel-css',
       'phui-header-view-css',
       'phabricator-nav-view-css',
       'phui-basic-nav-view-css',
       'phui-crumbs-view-css',
       'phui-oi-list-view-css',
       'phui-oi-color-css',
       'phui-oi-big-ui-css',
       'phui-oi-drag-ui-css',
       'phui-oi-simple-ui-css',
       'phui-oi-flush-ui-css',
       'global-drag-and-drop-css',
       'phui-spacing-css',
       'phui-form-css',
       'phui-icon-view-css',
       'phabricator-action-list-view-css',
       'phui-property-list-view-css',
       'phui-tag-view-css',
       'phui-list-view-css',
       'font-fontawesome',
       'font-lato',
       'phui-font-icon-base-css',
       'phui-fontkit-css',
       'phui-box-css',
       'phui-object-box-css',
       'phui-timeline-view-css',
       'phui-two-column-view-css',
       'phui-curtain-view-css',
       'sprite-login-css',
       'sprite-tokens-css',
       'tokens-css',
       'auth-css',
       'phui-status-list-view-css',
       'phui-feed-story-css',
       'phabricator-feed-css',
       'phabricator-dashboard-css',
       'aphront-multi-column-view-css',
       'phui-curtain-object-ref-view-css',
       'phui-comment-form-css',
       'phui-head-thing-view-css',
       'conpherence-durable-column-view',
       'phui-button-bar-css',
     ),
     'core.pkg.js' => array(
       'javelin-util',
       'javelin-install',
       'javelin-event',
       'javelin-stratcom',
       'javelin-behavior',
       'javelin-resource',
       'javelin-request',
       'javelin-vector',
       'javelin-dom',
       'javelin-json',
       'javelin-uri',
       'javelin-workflow',
       'javelin-mask',
       'javelin-typeahead',
       'javelin-typeahead-normalizer',
       'javelin-typeahead-source',
       'javelin-typeahead-preloaded-source',
       'javelin-typeahead-ondemand-source',
       'javelin-tokenizer',
       'javelin-history',
       'javelin-router',
       'javelin-routable',
       'javelin-behavior-aphront-basic-tokenizer',
       'javelin-behavior-workflow',
       'javelin-behavior-aphront-form-disable-on-submit',
       'phabricator-keyboard-shortcut-manager',
       'phabricator-keyboard-shortcut',
       'javelin-behavior-phabricator-keyboard-shortcuts',
       'javelin-behavior-refresh-csrf',
       'javelin-behavior-phabricator-watch-anchor',
       'javelin-behavior-phabricator-autofocus',
       'phuix-dropdown-menu',
       'phuix-action-list-view',
       'phuix-action-view',
       'phuix-icon-view',
       'phabricator-phtize',
       'javelin-behavior-phabricator-oncopy',
       'phabricator-tooltip',
       'javelin-behavior-phabricator-tooltips',
       'phabricator-prefab',
       'javelin-behavior-device',
       'javelin-behavior-toggle-class',
       'javelin-behavior-lightbox-attachments',
       'phabricator-busy',
       'javelin-sound',
       'javelin-aphlict',
       'phabricator-notification',
       'javelin-behavior-aphlict-listen',
       'javelin-behavior-phabricator-search-typeahead',
       'javelin-behavior-aphlict-dropdown',
       'javelin-behavior-history-install',
       'javelin-behavior-phabricator-gesture',
       'javelin-behavior-phabricator-remarkup-assist',
       'phabricator-textareautils',
       'phabricator-file-upload',
       'javelin-behavior-global-drag-and-drop',
       'javelin-behavior-phabricator-reveal-content',
       'phui-hovercard',
       'phui-hovercard-list',
       'javelin-behavior-phui-hovercards',
       'javelin-color',
       'javelin-fx',
       'phabricator-draggable-list',
       'javelin-behavior-phabricator-transaction-list',
       'javelin-behavior-phabricator-show-older-transactions',
       'javelin-behavior-phui-dropdown-menu',
       'javelin-behavior-doorkeeper-tag',
       'phabricator-title',
       'javelin-leader',
       'javelin-websocket',
       'javelin-behavior-dashboard-async-panel',
       'javelin-behavior-dashboard-tab-panel',
       'javelin-quicksand',
       'javelin-behavior-quicksand-blacklist',
       'javelin-behavior-high-security-warning',
       'javelin-behavior-read-only-warning',
       'javelin-scrollbar',
       'javelin-behavior-scrollbar',
       'javelin-behavior-durable-column',
       'conpherence-thread-manager',
       'javelin-behavior-detect-timezone',
       'javelin-behavior-setup-check-https',
       'javelin-behavior-aphlict-status',
       'javelin-behavior-user-menu',
       'phabricator-favicon',
       'javelin-behavior-phui-tab-group',
       'javelin-behavior-phui-submenu',
       'phuix-button-view',
       'javelin-behavior-comment-actions',
       'phuix-form-control-view',
       'phuix-autocomplete',
     ),
     'dark-console.pkg.js' => array(
       'javelin-behavior-dark-console',
       'phabricator-darklog',
       'phabricator-darkmessage',
     ),
     'differential.pkg.css' => array(
       'differential-core-view-css',
       'differential-changeset-view-css',
       'differential-revision-history-css',
       'differential-revision-list-css',
       'differential-table-of-contents-css',
       'differential-revision-comment-css',
       'differential-revision-add-comment-css',
       'phabricator-object-selector-css',
       'phabricator-content-source-view-css',
       'inline-comment-summary-css',
       'phui-inline-comment-view-css',
       'diff-tree-view-css',
       'phui-formation-view-css',
     ),
     'differential.pkg.js' => array(
       'phabricator-drag-and-drop-file-upload',
       'phabricator-shaped-request',
       'javelin-behavior-differential-populate',
       'javelin-behavior-differential-diff-radios',
       'javelin-behavior-aphront-drag-and-drop-textarea',
       'javelin-behavior-phabricator-object-selector',
       'javelin-behavior-repository-crossreference',
       'javelin-behavior-aphront-more',
       'phabricator-diff-inline-content-state',
       'phabricator-diff-inline',
       'phabricator-diff-changeset',
       'phabricator-diff-changeset-list',
       'phabricator-diff-tree-view',
       'phabricator-diff-path-view',
       'phuix-formation-view',
       'phuix-formation-column-view',
       'phuix-formation-flank-view',
       'javelin-external-editor-link-engine',
     ),
     'diffusion.pkg.css' => array(
       'diffusion-icons-css',
     ),
     'diffusion.pkg.js' => array(
       'javelin-behavior-diffusion-pull-lastmodified',
       'javelin-behavior-diffusion-commit-graph',
       'javelin-behavior-audit-preview',
     ),
     'maniphest.pkg.css' => array(
       'maniphest-task-summary-css',
     ),
     'maniphest.pkg.js' => array(
       'javelin-behavior-maniphest-batch-selector',
       'javelin-behavior-maniphest-list-editor',
     ),
   ),
 );
diff --git a/src/applications/transactions/editengine/PhabricatorEditEngine.php b/src/applications/transactions/editengine/PhabricatorEditEngine.php
index d0bb1972b6..0472fd895a 100644
--- a/src/applications/transactions/editengine/PhabricatorEditEngine.php
+++ b/src/applications/transactions/editengine/PhabricatorEditEngine.php
@@ -1,2748 +1,2752 @@
 <?php
 
 
 /**
  * @task fields Managing Fields
  * @task text Display Text
  * @task config Edit Engine Configuration
  * @task uri Managing URIs
  * @task load Creating and Loading Objects
  * @task web Responding to Web Requests
  * @task edit Responding to Edit Requests
  * @task http Responding to HTTP Parameter Requests
  * @task conduit Responding to Conduit Requests
  */
 abstract class PhabricatorEditEngine
   extends Phobject
   implements PhabricatorPolicyInterface {
 
   const EDITENGINECONFIG_DEFAULT = 'default';
 
   const SUBTYPE_DEFAULT = 'default';
 
   private $viewer;
   private $controller;
   private $isCreate;
   private $editEngineConfiguration;
   private $contextParameters = array();
   private $targetObject;
   private $page;
   private $pages;
   private $navigation;
 
   final public function setViewer(PhabricatorUser $viewer) {
     $this->viewer = $viewer;
     return $this;
   }
 
   final public function getViewer() {
     return $this->viewer;
   }
 
   final public function setController(PhabricatorController $controller) {
     $this->controller = $controller;
     $this->setViewer($controller->getViewer());
     return $this;
   }
 
   final public function getController() {
     return $this->controller;
   }
 
   final public function getEngineKey() {
     $key = $this->getPhobjectClassConstant('ENGINECONST', 64);
     if (strpos($key, '/') !== false) {
       throw new Exception(
         pht(
           'EditEngine ("%s") contains an invalid key character "/".',
           get_class($this)));
     }
     return $key;
   }
 
   final public function getApplication() {
     $app_class = $this->getEngineApplicationClass();
     return PhabricatorApplication::getByClass($app_class);
   }
 
   final public function addContextParameter($key) {
     $this->contextParameters[] = $key;
     return $this;
   }
 
   public function isEngineConfigurable() {
     return true;
   }
 
   public function isEngineExtensible() {
     return true;
   }
 
   public function isDefaultQuickCreateEngine() {
     return false;
   }
 
   public function getDefaultQuickCreateFormKeys() {
     $keys = array();
 
     if ($this->isDefaultQuickCreateEngine()) {
       $keys[] = self::EDITENGINECONFIG_DEFAULT;
     }
 
     foreach ($keys as $idx => $key) {
       $keys[$idx] = $this->getEngineKey().'/'.$key;
     }
 
     return $keys;
   }
 
   public static function splitFullKey($full_key) {
     return explode('/', $full_key, 2);
   }
 
   public function getQuickCreateOrderVector() {
     return id(new PhutilSortVector())
       ->addString($this->getObjectCreateShortText());
   }
 
   /**
    * Force the engine to edit a particular object.
    */
   public function setTargetObject($target_object) {
     $this->targetObject = $target_object;
     return $this;
   }
 
   public function getTargetObject() {
     return $this->targetObject;
   }
 
   public function setNavigation(AphrontSideNavFilterView $navigation) {
     $this->navigation = $navigation;
     return $this;
   }
 
   public function getNavigation() {
     return $this->navigation;
   }
 
 
 /* -(  Managing Fields  )---------------------------------------------------- */
 
 
   abstract public function getEngineApplicationClass();
   abstract protected function buildCustomEditFields($object);
 
   public function getFieldsForConfig(
     PhabricatorEditEngineConfiguration $config) {
 
     $object = $this->newEditableObject();
 
     $this->editEngineConfiguration = $config;
 
     // This is mostly making sure that we fill in default values.
     $this->setIsCreate(true);
 
     return $this->buildEditFields($object);
   }
 
   final protected function buildEditFields($object) {
     $viewer = $this->getViewer();
 
     $fields = $this->buildCustomEditFields($object);
 
     foreach ($fields as $field) {
       $field
         ->setViewer($viewer)
         ->setObject($object);
     }
 
     $fields = mpull($fields, null, 'getKey');
 
     if ($this->isEngineExtensible()) {
       $extensions = PhabricatorEditEngineExtension::getAllEnabledExtensions();
     } else {
       $extensions = array();
     }
 
     // See T13248. Create a template object to provide to extensions. We
     // adjust the template to have the intended subtype, so that extensions
     // may change behavior based on the form subtype.
 
     $template_object = clone $object;
     if ($this->getIsCreate()) {
       if ($this->supportsSubtypes()) {
         $config = $this->getEditEngineConfiguration();
         $subtype = $config->getSubtype();
         $template_object->setSubtype($subtype);
       }
     }
 
     foreach ($extensions as $extension) {
       $extension->setViewer($viewer);
 
       if (!$extension->supportsObject($this, $template_object)) {
         continue;
       }
 
       $extension_fields = $extension->buildCustomEditFields(
         $this,
         $template_object);
 
       // TODO: Validate this in more detail with a more tailored error.
       assert_instances_of($extension_fields, 'PhabricatorEditField');
 
       foreach ($extension_fields as $field) {
         $field
           ->setViewer($viewer)
           ->setObject($object);
 
         $group_key = $field->getBulkEditGroupKey();
         if ($group_key === null) {
           $field->setBulkEditGroupKey('extension');
         }
       }
 
       $extension_fields = mpull($extension_fields, null, 'getKey');
 
       foreach ($extension_fields as $key => $field) {
         $fields[$key] = $field;
       }
     }
 
     $config = $this->getEditEngineConfiguration();
     $fields = $this->willConfigureFields($object, $fields);
     $fields = $config->applyConfigurationToFields($this, $object, $fields);
 
     $fields = $this->applyPageToFields($object, $fields);
 
     return $fields;
   }
 
   protected function willConfigureFields($object, array $fields) {
     return $fields;
   }
 
   final public function supportsSubtypes() {
     try {
       $object = $this->newEditableObject();
     } catch (Exception $ex) {
       return false;
     }
 
     return ($object instanceof PhabricatorEditEngineSubtypeInterface);
   }
 
   final public function newSubtypeMap() {
     return $this->newEditableObject()->newEditEngineSubtypeMap();
   }
 
 
 /* -(  Display Text  )------------------------------------------------------- */
 
 
   /**
    * @task text
    */
   abstract public function getEngineName();
 
 
   /**
    * @task text
    */
   abstract protected function getObjectCreateTitleText($object);
 
   /**
    * @task text
    */
   protected function getFormHeaderText($object) {
     $config = $this->getEditEngineConfiguration();
     return $config->getName();
   }
 
   /**
    * @task text
    */
   abstract protected function getObjectEditTitleText($object);
 
 
   /**
    * @task text
    */
   abstract protected function getObjectCreateShortText();
 
 
   /**
    * @task text
    */
   abstract protected function getObjectName();
 
 
   /**
    * @task text
    */
   abstract protected function getObjectEditShortText($object);
 
 
   /**
    * @task text
    */
   protected function getObjectCreateButtonText($object) {
     return $this->getObjectCreateTitleText($object);
   }
 
 
   /**
    * @task text
    */
   protected function getObjectEditButtonText($object) {
     return pht('Save Changes');
   }
 
 
   /**
    * @task text
    */
   protected function getCommentViewSeriousHeaderText($object) {
     return pht('Take Action');
   }
 
 
   /**
    * @task text
    */
   protected function getCommentViewSeriousButtonText($object) {
     return pht('Submit');
   }
 
 
   /**
    * @task text
    */
   protected function getCommentViewHeaderText($object) {
     return $this->getCommentViewSeriousHeaderText($object);
   }
 
 
   /**
    * @task text
    */
   protected function getCommentViewButtonText($object) {
     return $this->getCommentViewSeriousButtonText($object);
   }
 
 
   /**
    * @task text
    */
   protected function getPageHeader($object) {
     return null;
   }
 
 
 
   /**
    * Return a human-readable header describing what this engine is used to do,
    * like "Configure Maniphest Task Forms".
    *
    * @return string Human-readable description of the engine.
    * @task text
    */
   abstract public function getSummaryHeader();
 
 
   /**
    * Return a human-readable summary of what this engine is used to do.
    *
    * @return string Human-readable description of the engine.
    * @task text
    */
   abstract public function getSummaryText();
 
 
 
 
 /* -(  Edit Engine Configuration  )------------------------------------------ */
 
 
   protected function supportsEditEngineConfiguration() {
     return true;
   }
 
   final protected function getEditEngineConfiguration() {
     return $this->editEngineConfiguration;
   }
 
   public function newConfigurationQuery() {
     return id(new PhabricatorEditEngineConfigurationQuery())
       ->setViewer($this->getViewer())
       ->withEngineKeys(array($this->getEngineKey()));
   }
 
   private function loadEditEngineConfigurationWithQuery(
     PhabricatorEditEngineConfigurationQuery $query,
     $sort_method) {
 
     if ($sort_method) {
       $results = $query->execute();
       $results = msort($results, $sort_method);
       $result = head($results);
     } else {
       $result = $query->executeOne();
     }
 
     if (!$result) {
       return null;
     }
 
     $this->editEngineConfiguration = $result;
     return $result;
   }
 
   private function loadEditEngineConfigurationWithIdentifier($identifier) {
     $query = $this->newConfigurationQuery()
       ->withIdentifiers(array($identifier));
 
     return $this->loadEditEngineConfigurationWithQuery($query, null);
   }
 
   private function loadDefaultConfiguration() {
     $query = $this->newConfigurationQuery()
       ->withIdentifiers(
         array(
           self::EDITENGINECONFIG_DEFAULT,
         ))
       ->withIgnoreDatabaseConfigurations(true);
 
     return $this->loadEditEngineConfigurationWithQuery($query, null);
   }
 
   private function loadDefaultCreateConfiguration() {
     $query = $this->newConfigurationQuery()
       ->withIsDefault(true)
       ->withIsDisabled(false);
 
     return $this->loadEditEngineConfigurationWithQuery(
       $query,
       'getCreateSortKey');
   }
 
   public function loadDefaultEditConfiguration($object) {
     $query = $this->newConfigurationQuery()
       ->withIsEdit(true)
       ->withIsDisabled(false);
 
     // If this object supports subtyping, we edit it with a form of the same
     // subtype: so "bug" tasks get edited with "bug" forms.
     if ($object instanceof PhabricatorEditEngineSubtypeInterface) {
       $query->withSubtypes(
         array(
           $object->getEditEngineSubtype(),
         ));
     }
 
     return $this->loadEditEngineConfigurationWithQuery(
       $query,
       'getEditSortKey');
   }
 
   final public function getBuiltinEngineConfigurations() {
     $configurations = $this->newBuiltinEngineConfigurations();
 
     if (!$configurations) {
       throw new Exception(
         pht(
           'EditEngine ("%s") returned no builtin engine configurations, but '.
           'an edit engine must have at least one configuration.',
           get_class($this)));
     }
 
     assert_instances_of($configurations, 'PhabricatorEditEngineConfiguration');
 
     $has_default = false;
     foreach ($configurations as $config) {
       if ($config->getBuiltinKey() == self::EDITENGINECONFIG_DEFAULT) {
         $has_default = true;
       }
     }
 
     if (!$has_default) {
       $first = head($configurations);
       if (!$first->getBuiltinKey()) {
         $first
           ->setBuiltinKey(self::EDITENGINECONFIG_DEFAULT)
           ->setIsDefault(true)
           ->setIsEdit(true);
 
         $first_name = $first->getName();
 
         if ($first_name === null || $first_name === '') {
           $first->setName($this->getObjectCreateShortText());
         }
     } else {
         throw new Exception(
           pht(
             'EditEngine ("%s") returned builtin engine configurations, '.
             'but none are marked as default and the first configuration has '.
             'a different builtin key already. Mark a builtin as default or '.
             'omit the key from the first configuration',
             get_class($this)));
       }
     }
 
     $builtins = array();
     foreach ($configurations as $key => $config) {
       $builtin_key = $config->getBuiltinKey();
 
       if ($builtin_key === null) {
         throw new Exception(
           pht(
             'EditEngine ("%s") returned builtin engine configurations, '.
             'but one (with key "%s") is missing a builtin key. Provide a '.
             'builtin key for each configuration (you can omit it from the '.
             'first configuration in the list to automatically assign the '.
             'default key).',
             get_class($this),
             $key));
       }
 
       if (isset($builtins[$builtin_key])) {
         throw new Exception(
           pht(
             'EditEngine ("%s") returned builtin engine configurations, '.
             'but at least two specify the same builtin key ("%s"). Engines '.
             'must have unique builtin keys.',
             get_class($this),
             $builtin_key));
       }
 
       $builtins[$builtin_key] = $config;
     }
 
 
     return $builtins;
   }
 
   protected function newBuiltinEngineConfigurations() {
     return array(
       $this->newConfiguration(),
     );
   }
 
   final protected function newConfiguration() {
     return PhabricatorEditEngineConfiguration::initializeNewConfiguration(
       $this->getViewer(),
       $this);
   }
 
 
 /* -(  Managing URIs  )------------------------------------------------------ */
 
 
   /**
    * @task uri
    */
   abstract protected function getObjectViewURI($object);
 
 
   /**
    * @task uri
    */
   protected function getObjectCreateCancelURI($object) {
     return $this->getApplication()->getApplicationURI();
   }
 
 
   /**
    * @task uri
    */
   protected function getEditorURI() {
     return $this->getApplication()->getApplicationURI('edit/');
   }
 
 
   /**
    * @task uri
    */
   protected function getObjectEditCancelURI($object) {
     return $this->getObjectViewURI($object);
   }
 
   /**
    * @task uri
    */
   public function getCreateURI($form_key) {
     try {
       $create_uri = $this->getEditURI(null, "form/{$form_key}/");
     } catch (Exception $ex) {
       $create_uri = null;
     }
 
     return $create_uri;
   }
 
   /**
    * @task uri
    */
   public function getEditURI($object = null, $path = null) {
     $parts = array();
 
     $parts[] = $this->getEditorURI();
 
     if ($object && $object->getID()) {
       $parts[] = $object->getID().'/';
     }
 
     if ($path !== null) {
       $parts[] = $path;
     }
 
     return implode('', $parts);
   }
 
   public function getEffectiveObjectViewURI($object) {
     if ($this->getIsCreate()) {
       return $this->getObjectViewURI($object);
     }
 
     $page = $this->getSelectedPage();
     if ($page) {
       $view_uri = $page->getViewURI();
       if ($view_uri !== null) {
         return $view_uri;
       }
     }
 
     return $this->getObjectViewURI($object);
   }
 
   public function getEffectiveObjectEditDoneURI($object) {
     return $this->getEffectiveObjectViewURI($object);
   }
 
   public function getEffectiveObjectEditCancelURI($object) {
     $page = $this->getSelectedPage();
     if ($page) {
       $view_uri = $page->getViewURI();
       if ($view_uri !== null) {
         return $view_uri;
       }
     }
 
     return $this->getObjectEditCancelURI($object);
   }
 
 
 /* -(  Creating and Loading Objects  )--------------------------------------- */
 
 
   /**
    * Initialize a new object for creation.
    *
    * @return object Newly initialized object.
    * @task load
    */
   abstract protected function newEditableObject();
 
 
   /**
    * Build an empty query for objects.
    *
    * @return PhabricatorPolicyAwareQuery Query.
    * @task load
    */
   abstract protected function newObjectQuery();
 
 
   /**
    * Test if this workflow is creating a new object or editing an existing one.
    *
    * @return bool True if a new object is being created.
    * @task load
    */
   final public function getIsCreate() {
     return $this->isCreate;
   }
 
   /**
    * Initialize a new object for object creation via Conduit.
    *
    * @return object Newly initialized object.
    * @param list<wild> Raw transactions.
    * @task load
    */
   protected function newEditableObjectFromConduit(array $raw_xactions) {
     return $this->newEditableObject();
   }
 
   /**
    * Initialize a new object for documentation creation.
    *
    * @return object Newly initialized object.
    * @task load
    */
   protected function newEditableObjectForDocumentation() {
     return $this->newEditableObject();
   }
 
   /**
    * Flag this workflow as a create or edit.
    *
    * @param bool True if this is a create workflow.
    * @return this
    * @task load
    */
   private function setIsCreate($is_create) {
     $this->isCreate = $is_create;
     return $this;
   }
 
 
   /**
    * Try to load an object by ID, PHID, or monogram. This is done primarily
    * to make Conduit a little easier to use.
    *
    * @param wild ID, PHID, or monogram.
    * @param list<const> List of required capability constants, or omit for
    *   defaults.
    * @return object Corresponding editable object.
    * @task load
    */
   private function newObjectFromIdentifier(
     $identifier,
     array $capabilities = array()) {
     if (is_int($identifier) || ctype_digit($identifier)) {
       $object = $this->newObjectFromID($identifier, $capabilities);
 
       if (!$object) {
         throw new Exception(
           pht(
             'No object exists with ID "%s".',
             $identifier));
       }
 
       return $object;
     }
 
     $type_unknown = PhabricatorPHIDConstants::PHID_TYPE_UNKNOWN;
     if (phid_get_type($identifier) != $type_unknown) {
       $object = $this->newObjectFromPHID($identifier, $capabilities);
 
       if (!$object) {
         throw new Exception(
           pht(
             'No object exists with PHID "%s".',
             $identifier));
       }
 
       return $object;
     }
 
     $target = id(new PhabricatorObjectQuery())
       ->setViewer($this->getViewer())
       ->withNames(array($identifier))
       ->executeOne();
     if (!$target) {
       throw new Exception(
         pht(
           'Monogram "%s" does not identify a valid object.',
           $identifier));
     }
 
     $expect = $this->newEditableObject();
     $expect_class = get_class($expect);
     $target_class = get_class($target);
     if ($expect_class !== $target_class) {
       throw new Exception(
         pht(
           'Monogram "%s" identifies an object of the wrong type. Loaded '.
           'object has class "%s", but this editor operates on objects of '.
           'type "%s".',
           $identifier,
           $target_class,
           $expect_class));
     }
 
     // Load the object by PHID using this engine's standard query. This makes
     // sure it's really valid, goes through standard policy check logic, and
     // picks up any `need...()` clauses we want it to load with.
 
     $object = $this->newObjectFromPHID($target->getPHID(), $capabilities);
     if (!$object) {
       throw new Exception(
         pht(
           'Failed to reload object identified by monogram "%s" when '.
           'querying by PHID.',
           $identifier));
     }
 
     return $object;
   }
 
   /**
    * Load an object by ID.
    *
    * @param int Object ID.
    * @param list<const> List of required capability constants, or omit for
    *   defaults.
    * @return object|null Object, or null if no such object exists.
    * @task load
    */
   private function newObjectFromID($id, array $capabilities = array()) {
     $query = $this->newObjectQuery()
       ->withIDs(array($id));
 
     return $this->newObjectFromQuery($query, $capabilities);
   }
 
 
   /**
    * Load an object by PHID.
    *
    * @param phid Object PHID.
    * @param list<const> List of required capability constants, or omit for
    *   defaults.
    * @return object|null Object, or null if no such object exists.
    * @task load
    */
   private function newObjectFromPHID($phid, array $capabilities = array()) {
     $query = $this->newObjectQuery()
       ->withPHIDs(array($phid));
 
     return $this->newObjectFromQuery($query, $capabilities);
   }
 
 
   /**
    * Load an object given a configured query.
    *
    * @param PhabricatorPolicyAwareQuery Configured query.
    * @param list<const> List of required capability constants, or omit for
    *  defaults.
    * @return object|null Object, or null if no such object exists.
    * @task load
    */
   private function newObjectFromQuery(
     PhabricatorPolicyAwareQuery $query,
     array $capabilities = array()) {
 
     $viewer = $this->getViewer();
 
     if (!$capabilities) {
       $capabilities = array(
         PhabricatorPolicyCapability::CAN_VIEW,
         PhabricatorPolicyCapability::CAN_EDIT,
       );
     }
 
     $object = $query
       ->setViewer($viewer)
       ->requireCapabilities($capabilities)
       ->executeOne();
     if (!$object) {
       return null;
     }
 
     return $object;
   }
 
 
   /**
    * Verify that an object is appropriate for editing.
    *
    * @param wild Loaded value.
    * @return void
    * @task load
    */
   private function validateObject($object) {
     if (!$object || !is_object($object)) {
       throw new Exception(
         pht(
           'EditEngine "%s" created or loaded an invalid object: object must '.
           'actually be an object, but is of some other type ("%s").',
           get_class($this),
           gettype($object)));
     }
 
     if (!($object instanceof PhabricatorApplicationTransactionInterface)) {
       throw new Exception(
         pht(
           'EditEngine "%s" created or loaded an invalid object: object (of '.
           'class "%s") must implement "%s", but does not.',
           get_class($this),
           get_class($object),
           'PhabricatorApplicationTransactionInterface'));
     }
   }
 
 
 /* -(  Responding to Web Requests  )----------------------------------------- */
 
 
   final public function buildResponse() {
     $viewer = $this->getViewer();
     $controller = $this->getController();
     $request = $controller->getRequest();
 
     $action = $this->getEditAction();
 
     $capabilities = array();
     $use_default = false;
     $require_create = true;
     switch ($action) {
       case 'comment':
         $capabilities = array(
           PhabricatorPolicyCapability::CAN_VIEW,
         );
         $use_default = true;
         break;
       case 'parameters':
         $use_default = true;
         break;
       case 'nodefault':
       case 'nocreate':
       case 'nomanage':
         $require_create = false;
         break;
       default:
         break;
     }
 
     $object = $this->getTargetObject();
     if (!$object) {
       $id = $request->getURIData('id');
 
       if ($id) {
         $this->setIsCreate(false);
         $object = $this->newObjectFromID($id, $capabilities);
         if (!$object) {
           return new Aphront404Response();
         }
       } else {
         // Make sure the viewer has permission to create new objects of
         // this type if we're going to create a new object.
         if ($require_create) {
           $this->requireCreateCapability();
         }
 
         $this->setIsCreate(true);
         $object = $this->newEditableObject();
       }
     } else {
       $id = $object->getID();
     }
 
     $this->validateObject($object);
 
     if ($use_default) {
       $config = $this->loadDefaultConfiguration();
       if (!$config) {
         return new Aphront404Response();
       }
     } else {
       $form_key = $request->getURIData('formKey');
       if (strlen($form_key)) {
         $config = $this->loadEditEngineConfigurationWithIdentifier($form_key);
 
         if (!$config) {
           return new Aphront404Response();
         }
 
         if ($id && !$config->getIsEdit()) {
           return $this->buildNotEditFormRespose($object, $config);
         }
       } else {
         if ($id) {
           $config = $this->loadDefaultEditConfiguration($object);
           if (!$config) {
             return $this->buildNoEditResponse($object);
           }
         } else {
           $config = $this->loadDefaultCreateConfiguration();
           if (!$config) {
             return $this->buildNoCreateResponse($object);
           }
         }
       }
     }
 
     if ($config->getIsDisabled()) {
       return $this->buildDisabledFormResponse($object, $config);
     }
 
     $page_key = $request->getURIData('pageKey');
     if (!strlen($page_key)) {
       $pages = $this->getPages($object);
       if ($pages) {
         $page_key = head_key($pages);
       }
     }
 
     if (strlen($page_key)) {
       $page = $this->selectPage($object, $page_key);
       if (!$page) {
         return new Aphront404Response();
       }
     }
 
     switch ($action) {
       case 'parameters':
         return $this->buildParametersResponse($object);
       case 'nodefault':
         return $this->buildNoDefaultResponse($object);
       case 'nocreate':
         return $this->buildNoCreateResponse($object);
       case 'nomanage':
         return $this->buildNoManageResponse($object);
       case 'comment':
         return $this->buildCommentResponse($object);
       default:
         return $this->buildEditResponse($object);
     }
   }
 
   private function buildCrumbs($object, $final = false) {
     $controller = $this->getController();
 
     $crumbs = $controller->buildApplicationCrumbsForEditEngine();
     if ($this->getIsCreate()) {
       $create_text = $this->getObjectCreateShortText();
       if ($final) {
         $crumbs->addTextCrumb($create_text);
       } else {
         $edit_uri = $this->getEditURI($object);
         $crumbs->addTextCrumb($create_text, $edit_uri);
       }
     } else {
       $crumbs->addTextCrumb(
         $this->getObjectEditShortText($object),
         $this->getEffectiveObjectViewURI($object));
 
       $edit_text = pht('Edit');
       if ($final) {
         $crumbs->addTextCrumb($edit_text);
       } else {
         $edit_uri = $this->getEditURI($object);
         $crumbs->addTextCrumb($edit_text, $edit_uri);
       }
     }
 
     return $crumbs;
   }
 
   private function buildEditResponse($object) {
     $viewer = $this->getViewer();
     $controller = $this->getController();
     $request = $controller->getRequest();
 
     $fields = $this->buildEditFields($object);
     $template = $object->getApplicationTransactionTemplate();
 
     $page_state = new PhabricatorEditEnginePageState();
 
     if ($this->getIsCreate()) {
       $cancel_uri = $this->getObjectCreateCancelURI($object);
       $submit_button = $this->getObjectCreateButtonText($object);
 
       $page_state->setIsCreate(true);
     } else {
       $cancel_uri = $this->getEffectiveObjectEditCancelURI($object);
       $submit_button = $this->getObjectEditButtonText($object);
     }
 
     $config = $this->getEditEngineConfiguration()
       ->attachEngine($this);
 
     // NOTE: Don't prompt users to override locks when creating objects,
     // even if the default settings would create a locked object.
 
     $can_interact = PhabricatorPolicyFilter::canInteract($viewer, $object);
     if (!$can_interact &&
         !$this->getIsCreate() &&
         !$request->getBool('editEngine') &&
         !$request->getBool('overrideLock')) {
 
       $lock = PhabricatorEditEngineLock::newForObject($viewer, $object);
 
       $dialog = $this->getController()
         ->newDialog()
         ->addHiddenInput('overrideLock', true)
         ->setDisableWorkflowOnSubmit(true)
         ->addCancelButton($cancel_uri);
 
       return $lock->willPromptUserForLockOverrideWithDialog($dialog);
     }
 
     $validation_exception = null;
     if ($request->isFormOrHisecPost() && $request->getBool('editEngine')) {
       $page_state->setIsSubmit(true);
 
       $submit_fields = $fields;
 
       foreach ($submit_fields as $key => $field) {
         if (!$field->shouldGenerateTransactionsFromSubmit()) {
           unset($submit_fields[$key]);
           continue;
         }
       }
 
       // Before we read the submitted values, store a copy of what we would
       // use if the form was empty so we can figure out which transactions are
       // just setting things to their default values for the current form.
       $defaults = array();
       foreach ($submit_fields as $key => $field) {
         $defaults[$key] = $field->getValueForTransaction();
       }
 
       foreach ($submit_fields as $key => $field) {
         $field->setIsSubmittedForm(true);
 
         if (!$field->shouldReadValueFromSubmit()) {
           continue;
         }
 
         $field->readValueFromSubmit($request);
       }
 
       $xactions = array();
 
       if ($this->getIsCreate()) {
         $xactions[] = id(clone $template)
           ->setTransactionType(PhabricatorTransactions::TYPE_CREATE);
 
         if ($this->supportsSubtypes()) {
           $xactions[] = id(clone $template)
             ->setTransactionType(PhabricatorTransactions::TYPE_SUBTYPE)
             ->setNewValue($config->getSubtype());
         }
       }
 
       foreach ($submit_fields as $key => $field) {
         $field_value = $field->getValueForTransaction();
 
         $type_xactions = $field->generateTransactions(
           clone $template,
           array(
             'value' => $field_value,
           ));
 
         foreach ($type_xactions as $type_xaction) {
           $default = $defaults[$key];
 
           if ($default === $field->getValueForTransaction()) {
             $type_xaction->setIsDefaultTransaction(true);
           }
 
           $xactions[] = $type_xaction;
         }
       }
 
       $editor = $object->getApplicationTransactionEditor()
         ->setActor($viewer)
         ->setContentSourceFromRequest($request)
         ->setCancelURI($cancel_uri)
         ->setContinueOnNoEffect(true);
 
       try {
         $xactions = $this->willApplyTransactions($object, $xactions);
 
         $editor->applyTransactions($object, $xactions);
 
         $this->didApplyTransactions($object, $xactions);
 
         return $this->newEditResponse($request, $object, $xactions);
       } catch (PhabricatorApplicationTransactionValidationException $ex) {
         $validation_exception = $ex;
 
         foreach ($fields as $field) {
           $message = $this->getValidationExceptionShortMessage($ex, $field);
           if ($message === null) {
             continue;
           }
 
           $field->setControlError($message);
         }
 
         $page_state->setIsError(true);
       }
     } else {
       if ($this->getIsCreate()) {
         $template = $request->getStr('template');
 
         if (strlen($template)) {
           $template_object = $this->newObjectFromIdentifier(
             $template,
             array(
               PhabricatorPolicyCapability::CAN_VIEW,
             ));
           if (!$template_object) {
             return new Aphront404Response();
           }
         } else {
           $template_object = null;
         }
 
         if ($template_object) {
           $copy_fields = $this->buildEditFields($template_object);
           $copy_fields = mpull($copy_fields, null, 'getKey');
           foreach ($copy_fields as $copy_key => $copy_field) {
             if (!$copy_field->getIsCopyable()) {
               unset($copy_fields[$copy_key]);
             }
           }
         } else {
           $copy_fields = array();
         }
 
         foreach ($fields as $field) {
           if (!$field->shouldReadValueFromRequest()) {
             continue;
           }
 
           $field_key = $field->getKey();
           if (isset($copy_fields[$field_key])) {
             $field->readValueFromField($copy_fields[$field_key]);
           }
 
           $field->readValueFromRequest($request);
         }
       }
     }
 
     $action_button = $this->buildEditFormActionButton($object);
 
     if ($this->getIsCreate()) {
       $header_text = $this->getFormHeaderText($object);
     } else {
       $header_text = $this->getObjectEditTitleText($object);
     }
 
     $show_preview = !$request->isAjax();
 
     if ($show_preview) {
       $previews = array();
       foreach ($fields as $field) {
         $preview = $field->getPreviewPanel();
         if (!$preview) {
           continue;
         }
 
         $control_id = $field->getControlID();
 
         $preview
           ->setControlID($control_id)
           ->setPreviewURI('/transactions/remarkuppreview/');
 
         $previews[] = $preview;
       }
     } else {
       $previews = array();
     }
 
     $form = $this->buildEditForm($object, $fields);
 
     $crumbs = $this->buildCrumbs($object, $final = true);
     $crumbs->setBorder(true);
 
     if ($request->isAjax()) {
       return $this->getController()
         ->newDialog()
         ->setWidth(AphrontDialogView::WIDTH_FULL)
         ->setTitle($header_text)
         ->setValidationException($validation_exception)
         ->appendForm($form)
         ->addCancelButton($cancel_uri)
         ->addSubmitButton($submit_button);
     }
 
     $box_header = id(new PHUIHeaderView())
       ->setHeader($header_text);
 
     if ($action_button) {
       $box_header->addActionLink($action_button);
     }
 
     $request_submit_key = $request->getSubmitKey();
     $engine_submit_key = $this->getEditEngineSubmitKey();
 
     if ($request_submit_key === $engine_submit_key) {
       $page_state->setIsSubmit(true);
       $page_state->setIsSave(true);
     }
 
     $head = $this->newEditFormHeadContent($page_state);
     $tail = $this->newEditFormTailContent($page_state);
 
     $box = id(new PHUIObjectBoxView())
       ->setUser($viewer)
       ->setHeader($box_header)
       ->setValidationException($validation_exception)
       ->setBackground(PHUIObjectBoxView::WHITE_CONFIG)
       ->appendChild($form);
 
     $content = array(
       $head,
       $box,
       $previews,
       $tail,
     );
 
     $view = new PHUITwoColumnView();
 
     $page_header = $this->getPageHeader($object);
     if ($page_header) {
       $view->setHeader($page_header);
     }
 
     $view->setFooter($content);
 
     $page = $controller->newPage()
       ->setTitle($header_text)
       ->setCrumbs($crumbs)
       ->appendChild($view);
 
     $navigation = $this->getNavigation();
     if ($navigation) {
       $page->setNavigation($navigation);
     }
 
     return $page;
   }
 
   protected function newEditFormHeadContent(
     PhabricatorEditEnginePageState $state) {
     return null;
   }
 
   protected function newEditFormTailContent(
     PhabricatorEditEnginePageState $state) {
     return null;
   }
 
   protected function newEditResponse(
     AphrontRequest $request,
     $object,
     array $xactions) {
 
     $submit_cookie = PhabricatorCookies::COOKIE_SUBMIT;
     $submit_key = $this->getEditEngineSubmitKey();
 
     $request->setTemporaryCookie($submit_cookie, $submit_key);
 
     return id(new AphrontRedirectResponse())
       ->setURI($this->getEffectiveObjectEditDoneURI($object));
   }
 
   private function getEditEngineSubmitKey() {
     return 'edit-engine/'.$this->getEngineKey();
   }
 
   private function buildEditForm($object, array $fields) {
     $viewer = $this->getViewer();
     $controller = $this->getController();
     $request = $controller->getRequest();
 
     $fields = $this->willBuildEditForm($object, $fields);
 
     $request_path = $request->getPath();
 
     $form = id(new AphrontFormView())
       ->setUser($viewer)
       ->setAction($request_path)
       ->addHiddenInput('editEngine', 'true');
 
     foreach ($this->contextParameters as $param) {
       $form->addHiddenInput($param, $request->getStr($param));
     }
 
     $requires_mfa = false;
     if ($object instanceof PhabricatorEditEngineMFAInterface) {
       $mfa_engine = PhabricatorEditEngineMFAEngine::newEngineForObject($object)
         ->setViewer($viewer);
       $requires_mfa = $mfa_engine->shouldRequireMFA();
     }
 
     if ($requires_mfa) {
       $message = pht(
         'You will be required to provide multi-factor credentials to make '.
         'changes.');
       $form->appendChild(
         id(new PHUIInfoView())
           ->setSeverity(PHUIInfoView::SEVERITY_MFA)
           ->setErrors(array($message)));
 
       // TODO: This should also set workflow on the form, so the user doesn't
       // lose any form data if they "Cancel". However, Maniphest currently
       // overrides "newEditResponse()" if the request is Ajax and returns a
       // bag of view data. This can reasonably be cleaned up when workboards
       // get their next iteration.
     }
 
     foreach ($fields as $field) {
       if (!$field->getIsFormField()) {
         continue;
       }
 
       $field->appendToForm($form);
     }
 
     if ($this->getIsCreate()) {
       $cancel_uri = $this->getObjectCreateCancelURI($object);
       $submit_button = $this->getObjectCreateButtonText($object);
     } else {
       $cancel_uri = $this->getEffectiveObjectEditCancelURI($object);
       $submit_button = $this->getObjectEditButtonText($object);
     }
 
     if (!$request->isAjax()) {
       $buttons = id(new AphrontFormSubmitControl())
         ->setValue($submit_button);
 
       if ($cancel_uri) {
         $buttons->addCancelButton($cancel_uri);
       }
 
       $form->appendControl($buttons);
     }
 
     return $form;
   }
 
   protected function willBuildEditForm($object, array $fields) {
     return $fields;
   }
 
   private function buildEditFormActionButton($object) {
     if (!$this->isEngineConfigurable()) {
       return null;
     }
 
     $viewer = $this->getViewer();
 
     $action_view = id(new PhabricatorActionListView())
       ->setUser($viewer);
 
     foreach ($this->buildEditFormActions($object) as $action) {
       $action_view->addAction($action);
     }
 
     $action_button = id(new PHUIButtonView())
       ->setTag('a')
       ->setText(pht('Configure Form'))
       ->setHref('#')
       ->setIcon('fa-gear')
       ->setDropdownMenu($action_view);
 
     return $action_button;
   }
 
   private function buildEditFormActions($object) {
     $actions = array();
 
     if ($this->supportsEditEngineConfiguration()) {
       $engine_key = $this->getEngineKey();
       $config = $this->getEditEngineConfiguration();
 
       $can_manage = PhabricatorPolicyFilter::hasCapability(
         $this->getViewer(),
         $config,
         PhabricatorPolicyCapability::CAN_EDIT);
 
       if ($can_manage) {
         $manage_uri = $config->getURI();
       } else {
         $manage_uri = $this->getEditURI(null, 'nomanage/');
       }
 
       $view_uri = "/transactions/editengine/{$engine_key}/";
 
       $actions[] = id(new PhabricatorActionView())
         ->setLabel(true)
         ->setName(pht('Configuration'));
 
       $actions[] = id(new PhabricatorActionView())
         ->setName(pht('View Form Configurations'))
         ->setIcon('fa-list-ul')
         ->setHref($view_uri);
 
       $actions[] = id(new PhabricatorActionView())
         ->setName(pht('Edit Form Configuration'))
         ->setIcon('fa-pencil')
         ->setHref($manage_uri)
         ->setDisabled(!$can_manage)
         ->setWorkflow(!$can_manage);
     }
 
     $actions[] = id(new PhabricatorActionView())
       ->setLabel(true)
       ->setName(pht('Documentation'));
 
     $actions[] = id(new PhabricatorActionView())
       ->setName(pht('Using HTTP Parameters'))
       ->setIcon('fa-book')
       ->setHref($this->getEditURI($object, 'parameters/'));
 
     $doc_href = PhabricatorEnv::getDoclink('User Guide: Customizing Forms');
     $actions[] = id(new PhabricatorActionView())
       ->setName(pht('User Guide: Customizing Forms'))
       ->setIcon('fa-book')
       ->setHref($doc_href);
 
     return $actions;
   }
 
 
   public function newNUXButton($text) {
     $specs = $this->newCreateActionSpecifications(array());
     $head = head($specs);
 
     return id(new PHUIButtonView())
       ->setTag('a')
       ->setText($text)
       ->setHref($head['uri'])
       ->setDisabled($head['disabled'])
       ->setWorkflow($head['workflow'])
       ->setColor(PHUIButtonView::GREEN);
   }
 
 
   final public function addActionToCrumbs(
     PHUICrumbsView $crumbs,
     array $parameters = array()) {
     $viewer = $this->getViewer();
 
     $specs = $this->newCreateActionSpecifications($parameters);
 
     $head = head($specs);
     $menu_uri = $head['uri'];
 
     $dropdown = null;
     if (count($specs) > 1) {
       $menu_icon = 'fa-caret-square-o-down';
       $menu_name = $this->getObjectCreateShortText();
       $workflow = false;
       $disabled = false;
 
       $dropdown = id(new PhabricatorActionListView())
         ->setUser($viewer);
 
       foreach ($specs as $spec) {
         $dropdown->addAction(
           id(new PhabricatorActionView())
             ->setName($spec['name'])
             ->setIcon($spec['icon'])
             ->setHref($spec['uri'])
             ->setDisabled($head['disabled'])
             ->setWorkflow($head['workflow']));
       }
 
     } else {
       $menu_icon = $head['icon'];
       $menu_name = $head['name'];
 
       $workflow = $head['workflow'];
       $disabled = $head['disabled'];
     }
 
     $action = id(new PHUIListItemView())
       ->setName($menu_name)
       ->setHref($menu_uri)
       ->setIcon($menu_icon)
       ->setWorkflow($workflow)
       ->setDisabled($disabled);
 
     if ($dropdown) {
       $action->setDropdownMenu($dropdown);
     }
 
     $crumbs->addAction($action);
   }
 
 
   /**
    * Build a raw description of available "Create New Object" UI options so
    * other methods can build menus or buttons.
    */
   public function newCreateActionSpecifications(array $parameters) {
     $viewer = $this->getViewer();
 
     $can_create = $this->hasCreateCapability();
     if ($can_create) {
       $configs = $this->loadUsableConfigurationsForCreate();
     } else {
       $configs = array();
     }
 
     $disabled = false;
     $workflow = false;
 
     $menu_icon = 'fa-plus-square';
     $specs = array();
     if (!$configs) {
       if ($viewer->isLoggedIn()) {
         $disabled = true;
       } else {
         // If the viewer isn't logged in, assume they'll get hit with a login
         // dialog and are likely able to create objects after they log in.
         $disabled = false;
       }
       $workflow = true;
 
       if ($can_create) {
         $create_uri = $this->getEditURI(null, 'nodefault/');
       } else {
         $create_uri = $this->getEditURI(null, 'nocreate/');
       }
 
       $specs[] = array(
         'name' => $this->getObjectCreateShortText(),
         'uri' => $create_uri,
         'icon' => $menu_icon,
         'disabled' => $disabled,
         'workflow' => $workflow,
       );
     } else {
       foreach ($configs as $config) {
         $config_uri = $config->getCreateURI();
 
         if ($parameters) {
           $config_uri = (string)new PhutilURI($config_uri, $parameters);
         }
 
         $specs[] = array(
           'name' => $config->getDisplayName(),
           'uri' => $config_uri,
           'icon' => 'fa-plus',
           'disabled' => false,
           'workflow' => false,
         );
       }
     }
 
     return $specs;
   }
 
   final public function buildEditEngineCommentView($object) {
     $config = $this->loadDefaultEditConfiguration($object);
 
     if (!$config) {
       // TODO: This just nukes the entire comment form if you don't have access
       // to any edit forms. We might want to tailor this UX a bit.
       return id(new PhabricatorApplicationTransactionCommentView())
         ->setNoPermission(true);
     }
 
     $viewer = $this->getViewer();
 
     $can_interact = PhabricatorPolicyFilter::canInteract($viewer, $object);
     if (!$can_interact) {
       $lock = PhabricatorEditEngineLock::newForObject($viewer, $object);
 
       return id(new PhabricatorApplicationTransactionCommentView())
         ->setEditEngineLock($lock);
     }
 
     $object_phid = $object->getPHID();
     $is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
 
     if ($is_serious) {
       $header_text = $this->getCommentViewSeriousHeaderText($object);
       $button_text = $this->getCommentViewSeriousButtonText($object);
     } else {
       $header_text = $this->getCommentViewHeaderText($object);
       $button_text = $this->getCommentViewButtonText($object);
     }
 
     $comment_uri = $this->getEditURI($object, 'comment/');
 
     $requires_mfa = false;
     if ($object instanceof PhabricatorEditEngineMFAInterface) {
       $mfa_engine = PhabricatorEditEngineMFAEngine::newEngineForObject($object)
         ->setViewer($viewer);
       $requires_mfa = $mfa_engine->shouldRequireMFA();
     }
 
     $view = id(new PhabricatorApplicationTransactionCommentView())
       ->setUser($viewer)
       ->setObjectPHID($object_phid)
       ->setHeaderText($header_text)
       ->setAction($comment_uri)
       ->setRequiresMFA($requires_mfa)
       ->setSubmitButtonName($button_text);
 
     $draft = PhabricatorVersionedDraft::loadDraft(
       $object_phid,
       $viewer->getPHID());
     if ($draft) {
       $view->setVersionedDraft($draft);
     }
 
     $view->setCurrentVersion($this->loadDraftVersion($object));
 
     $fields = $this->buildEditFields($object);
 
     $can_edit = PhabricatorPolicyFilter::hasCapability(
       $viewer,
       $object,
       PhabricatorPolicyCapability::CAN_EDIT);
 
     $comment_actions = array();
     foreach ($fields as $field) {
       if (!$field->shouldGenerateTransactionsFromComment()) {
         continue;
       }
 
       if (!$can_edit) {
         if (!$field->getCanApplyWithoutEditCapability()) {
           continue;
         }
       }
 
       $comment_action = $field->getCommentAction();
       if (!$comment_action) {
         continue;
       }
 
       $key = $comment_action->getKey();
 
       // TODO: Validate these better.
 
       $comment_actions[$key] = $comment_action;
     }
 
     $comment_actions = msortv($comment_actions, 'getSortVector');
 
     $view->setCommentActions($comment_actions);
 
     $comment_groups = $this->newCommentActionGroups();
     $view->setCommentActionGroups($comment_groups);
 
     return $view;
   }
 
   protected function loadDraftVersion($object) {
     $viewer = $this->getViewer();
 
     if (!$viewer->isLoggedIn()) {
       return null;
     }
 
     $template = $object->getApplicationTransactionTemplate();
     $conn_r = $template->establishConnection('r');
 
     // Find the most recent transaction the user has written. We'll use this
     // as a version number to make sure that out-of-date drafts get discarded.
     $result = queryfx_one(
       $conn_r,
       'SELECT id AS version FROM %T
         WHERE objectPHID = %s AND authorPHID = %s
         ORDER BY id DESC LIMIT 1',
       $template->getTableName(),
       $object->getPHID(),
       $viewer->getPHID());
 
     if ($result) {
       return (int)$result['version'];
     } else {
       return null;
     }
   }
 
 
 /* -(  Responding to HTTP Parameter Requests  )------------------------------ */
 
 
   /**
    * Respond to a request for documentation on HTTP parameters.
    *
    * @param object Editable object.
    * @return AphrontResponse Response object.
    * @task http
    */
   private function buildParametersResponse($object) {
     $controller = $this->getController();
     $viewer = $this->getViewer();
     $request = $controller->getRequest();
     $fields = $this->buildEditFields($object);
 
     $crumbs = $this->buildCrumbs($object);
     $crumbs->addTextCrumb(pht('HTTP Parameters'));
     $crumbs->setBorder(true);
 
     $header_text = pht(
       'HTTP Parameters: %s',
       $this->getObjectCreateShortText());
 
     $header = id(new PHUIHeaderView())
       ->setHeader($header_text);
 
     $help_view = id(new PhabricatorApplicationEditHTTPParameterHelpView())
       ->setUser($viewer)
       ->setFields($fields);
 
     $document = id(new PHUIDocumentView())
       ->setUser($viewer)
       ->setHeader($header)
       ->appendChild($help_view);
 
     return $controller->newPage()
       ->setTitle(pht('HTTP Parameters'))
       ->setCrumbs($crumbs)
       ->appendChild($document);
   }
 
 
   private function buildError($object, $title, $body) {
     $cancel_uri = $this->getObjectCreateCancelURI($object);
 
     $dialog = $this->getController()
       ->newDialog()
       ->addCancelButton($cancel_uri);
 
     if ($title !== null) {
       $dialog->setTitle($title);
     }
 
     if ($body !== null) {
       $dialog->appendParagraph($body);
     }
 
     return $dialog;
   }
 
 
   private function buildNoDefaultResponse($object) {
     return $this->buildError(
       $object,
       pht('No Default Create Forms'),
       pht(
         'This application is not configured with any forms for creating '.
         'objects that are visible to you and enabled.'));
   }
 
   private function buildNoCreateResponse($object) {
     return $this->buildError(
       $object,
       pht('No Create Permission'),
       pht('You do not have permission to create these objects.'));
   }
 
   private function buildNoManageResponse($object) {
     return $this->buildError(
       $object,
       pht('No Manage Permission'),
       pht(
         'You do not have permission to configure forms for this '.
         'application.'));
   }
 
   private function buildNoEditResponse($object) {
     return $this->buildError(
       $object,
       pht('No Edit Forms'),
       pht(
         'You do not have access to any forms which are enabled and marked '.
         'as edit forms.'));
   }
 
   private function buildNotEditFormRespose($object, $config) {
     return $this->buildError(
       $object,
       pht('Not an Edit Form'),
       pht(
         'This form ("%s") is not marked as an edit form, so '.
         'it can not be used to edit objects.',
         $config->getName()));
   }
 
   private function buildDisabledFormResponse($object, $config) {
     return $this->buildError(
       $object,
       pht('Form Disabled'),
       pht(
         'This form ("%s") has been disabled, so it can not be used.',
         $config->getName()));
   }
 
   private function buildLockedObjectResponse($object) {
     $dialog = $this->buildError($object, null, null);
     $viewer = $this->getViewer();
 
     $lock = PhabricatorEditEngineLock::newForObject($viewer, $object);
     return $lock->willBlockUserInteractionWithDialog($dialog);
   }
 
   private function buildCommentResponse($object) {
     $viewer = $this->getViewer();
 
     if ($this->getIsCreate()) {
       return new Aphront404Response();
     }
 
     $controller = $this->getController();
     $request = $controller->getRequest();
 
     // NOTE: We handle hisec inside the transaction editor with "Sign With MFA"
     // comment actions.
     if (!$request->isFormOrHisecPost()) {
       return new Aphront400Response();
     }
 
     $can_interact = PhabricatorPolicyFilter::canInteract($viewer, $object);
     if (!$can_interact) {
       return $this->buildLockedObjectResponse($object);
     }
 
     $config = $this->loadDefaultEditConfiguration($object);
     if (!$config) {
       return new Aphront404Response();
     }
 
     $fields = $this->buildEditFields($object);
 
     $is_preview = $request->isPreviewRequest();
     $view_uri = $this->getEffectiveObjectViewURI($object);
 
     $template = $object->getApplicationTransactionTemplate();
     $comment_template = $template->getApplicationTransactionCommentObject();
 
     $comment_text = $request->getStr('comment');
 
+    $comment_metadata = $request->getStr('comment_metadata');
+    if (strlen($comment_metadata)) {
+      $comment_metadata = phutil_json_decode($comment_metadata);
+    }
+
     $actions = $request->getStr('editengine.actions');
     if ($actions) {
       $actions = phutil_json_decode($actions);
     }
 
     if ($is_preview) {
       $version_key = PhabricatorVersionedDraft::KEY_VERSION;
       $request_version = $request->getInt($version_key);
       $current_version = $this->loadDraftVersion($object);
       if ($request_version >= $current_version) {
         $draft = PhabricatorVersionedDraft::loadOrCreateDraft(
           $object->getPHID(),
           $viewer->getPHID(),
           $current_version);
 
-        $is_empty = (!strlen($comment_text) && !$actions);
-
         $draft
           ->setProperty('comment', $comment_text)
+          ->setProperty('metadata', $comment_metadata)
           ->setProperty('actions', $actions)
           ->save();
 
         $draft_engine = $this->newDraftEngine($object);
         if ($draft_engine) {
           $draft_engine
             ->setVersionedDraft($draft)
             ->synchronize();
         }
       }
     }
 
     $xactions = array();
 
     $can_edit = PhabricatorPolicyFilter::hasCapability(
       $viewer,
       $object,
       PhabricatorPolicyCapability::CAN_EDIT);
 
     if ($actions) {
       $action_map = array();
       foreach ($actions as $action) {
         $type = idx($action, 'type');
         if (!$type) {
           continue;
         }
 
         if (empty($fields[$type])) {
           continue;
         }
 
         $action_map[$type] = $action;
       }
 
       foreach ($action_map as $type => $action) {
         $field = $fields[$type];
 
         if (!$field->shouldGenerateTransactionsFromComment()) {
           continue;
         }
 
         // If you don't have edit permission on the object, you're limited in
         // which actions you can take via the comment form. Most actions
         // need edit permission, but some actions (like "Accept Revision")
         // can be applied by anyone with view permission.
         if (!$can_edit) {
           if (!$field->getCanApplyWithoutEditCapability()) {
             // We know the user doesn't have the capability, so this will
             // raise a policy exception.
             PhabricatorPolicyFilter::requireCapability(
               $viewer,
               $object,
               PhabricatorPolicyCapability::CAN_EDIT);
           }
         }
 
         if (array_key_exists('initialValue', $action)) {
           $field->setInitialValue($action['initialValue']);
         }
 
         $field->readValueFromComment(idx($action, 'value'));
 
         $type_xactions = $field->generateTransactions(
           clone $template,
           array(
             'value' => $field->getValueForTransaction(),
           ));
         foreach ($type_xactions as $type_xaction) {
           $xactions[] = $type_xaction;
         }
       }
     }
 
     $auto_xactions = $this->newAutomaticCommentTransactions($object);
     foreach ($auto_xactions as $xaction) {
       $xactions[] = $xaction;
     }
 
     if (strlen($comment_text) || !$xactions) {
       $xactions[] = id(clone $template)
         ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT)
         ->attachComment(
           id(clone $comment_template)
             ->setContent($comment_text));
     }
 
     $editor = $object->getApplicationTransactionEditor()
       ->setActor($viewer)
       ->setContinueOnNoEffect($request->isContinueRequest())
       ->setContinueOnMissingFields(true)
       ->setContentSourceFromRequest($request)
       ->setCancelURI($view_uri)
       ->setRaiseWarnings(!$request->getBool('editEngine.warnings'))
       ->setIsPreview($is_preview);
 
     try {
       $xactions = $editor->applyTransactions($object, $xactions);
     } catch (PhabricatorApplicationTransactionValidationException $ex) {
       return id(new PhabricatorApplicationTransactionValidationResponse())
         ->setCancelURI($view_uri)
         ->setException($ex);
     } catch (PhabricatorApplicationTransactionNoEffectException $ex) {
       return id(new PhabricatorApplicationTransactionNoEffectResponse())
         ->setCancelURI($view_uri)
         ->setException($ex);
     } catch (PhabricatorApplicationTransactionWarningException $ex) {
       return id(new PhabricatorApplicationTransactionWarningResponse())
         ->setObject($object)
         ->setCancelURI($view_uri)
         ->setException($ex);
     }
 
     if (!$is_preview) {
       PhabricatorVersionedDraft::purgeDrafts(
         $object->getPHID(),
         $viewer->getPHID());
 
       $draft_engine = $this->newDraftEngine($object);
       if ($draft_engine) {
         $draft_engine
           ->setVersionedDraft(null)
           ->synchronize();
       }
     }
 
     if ($request->isAjax() && $is_preview) {
       $preview_content = $this->newCommentPreviewContent($object, $xactions);
 
       $raw_view_data = $request->getStr('viewData');
       try {
         $view_data = phutil_json_decode($raw_view_data);
       } catch (Exception $ex) {
         $view_data = array();
       }
 
       return id(new PhabricatorApplicationTransactionResponse())
         ->setObject($object)
         ->setViewer($viewer)
         ->setTransactions($xactions)
         ->setIsPreview($is_preview)
         ->setViewData($view_data)
         ->setPreviewContent($preview_content);
     } else {
       return id(new AphrontRedirectResponse())
         ->setURI($view_uri);
     }
   }
 
   protected function newDraftEngine($object) {
     $viewer = $this->getViewer();
 
     if ($object instanceof PhabricatorDraftInterface) {
       $engine = $object->newDraftEngine();
     } else {
       $engine = new PhabricatorBuiltinDraftEngine();
     }
 
     return $engine
       ->setObject($object)
       ->setViewer($viewer);
   }
 
 
 /* -(  Conduit  )------------------------------------------------------------ */
 
 
   /**
    * Respond to a Conduit edit request.
    *
    * This method accepts a list of transactions to apply to an object, and
    * either edits an existing object or creates a new one.
    *
    * @task conduit
    */
   final public function buildConduitResponse(ConduitAPIRequest $request) {
     $viewer = $this->getViewer();
 
     $config = $this->loadDefaultConfiguration();
     if (!$config) {
       throw new Exception(
         pht(
           'Unable to load configuration for this EditEngine ("%s").',
           get_class($this)));
     }
 
     $raw_xactions = $this->getRawConduitTransactions($request);
 
     $identifier = $request->getValue('objectIdentifier');
     if ($identifier) {
       $this->setIsCreate(false);
 
       // After T13186, each transaction can individually weaken or replace the
       // capabilities required to apply it, so we no longer need CAN_EDIT to
       // attempt to apply transactions to objects. In practice, almost all
       // transactions require CAN_EDIT so we won't get very far if we don't
       // have it.
       $capabilities = array(
         PhabricatorPolicyCapability::CAN_VIEW,
       );
 
       $object = $this->newObjectFromIdentifier(
         $identifier,
         $capabilities);
     } else {
       $this->requireCreateCapability();
 
       $this->setIsCreate(true);
       $object = $this->newEditableObjectFromConduit($raw_xactions);
     }
 
     $this->validateObject($object);
 
     $fields = $this->buildEditFields($object);
 
     $types = $this->getConduitEditTypesFromFields($fields);
     $template = $object->getApplicationTransactionTemplate();
 
     $xactions = $this->getConduitTransactions(
       $request,
       $raw_xactions,
       $types,
       $template);
 
     $editor = $object->getApplicationTransactionEditor()
       ->setActor($viewer)
       ->setContentSource($request->newContentSource())
       ->setContinueOnNoEffect(true);
 
     if (!$this->getIsCreate()) {
       $editor->setContinueOnMissingFields(true);
     }
 
     $xactions = $editor->applyTransactions($object, $xactions);
 
     $xactions_struct = array();
     foreach ($xactions as $xaction) {
       $xactions_struct[] = array(
         'phid' => $xaction->getPHID(),
       );
     }
 
     return array(
       'object' => array(
         'id' => (int)$object->getID(),
         'phid' => $object->getPHID(),
       ),
       'transactions' => $xactions_struct,
     );
   }
 
   private function getRawConduitTransactions(ConduitAPIRequest $request) {
     $transactions_key = 'transactions';
 
     $xactions = $request->getValue($transactions_key);
     if (!is_array($xactions)) {
       throw new Exception(
         pht(
           'Parameter "%s" is not a list of transactions.',
           $transactions_key));
     }
 
     foreach ($xactions as $key => $xaction) {
       if (!is_array($xaction)) {
         throw new Exception(
           pht(
             'Parameter "%s" must contain a list of transaction descriptions, '.
             'but item with key "%s" is not a dictionary.',
             $transactions_key,
             $key));
       }
 
       if (!array_key_exists('type', $xaction)) {
         throw new Exception(
           pht(
             'Parameter "%s" must contain a list of transaction descriptions, '.
             'but item with key "%s" is missing a "type" field. Each '.
             'transaction must have a type field.',
             $transactions_key,
             $key));
       }
 
       if (!array_key_exists('value', $xaction)) {
         throw new Exception(
           pht(
             'Parameter "%s" must contain a list of transaction descriptions, '.
             'but item with key "%s" is missing a "value" field. Each '.
             'transaction must have a value field.',
             $transactions_key,
             $key));
       }
     }
 
     return $xactions;
   }
 
 
   /**
    * Generate transactions which can be applied from edit actions in a Conduit
    * request.
    *
    * @param ConduitAPIRequest The request.
    * @param list<wild> Raw conduit transactions.
    * @param list<PhabricatorEditType> Supported edit types.
    * @param PhabricatorApplicationTransaction Template transaction.
    * @return list<PhabricatorApplicationTransaction> Generated transactions.
    * @task conduit
    */
   private function getConduitTransactions(
     ConduitAPIRequest $request,
     array $xactions,
     array $types,
     PhabricatorApplicationTransaction $template) {
 
     $viewer = $request->getUser();
     $results = array();
 
     foreach ($xactions as $key => $xaction) {
       $type = $xaction['type'];
       if (empty($types[$type])) {
         throw new Exception(
           pht(
             'Transaction with key "%s" has invalid type "%s". This type is '.
             'not recognized. Valid types are: %s.',
             $key,
             $type,
             implode(', ', array_keys($types))));
       }
     }
 
     if ($this->getIsCreate()) {
       $results[] = id(clone $template)
         ->setTransactionType(PhabricatorTransactions::TYPE_CREATE);
     }
 
     $is_strict = $request->getIsStrictlyTyped();
 
     foreach ($xactions as $xaction) {
       $type = $types[$xaction['type']];
 
       // Let the parameter type interpret the value. This allows you to
       // use usernames in list<user> fields, for example.
       $parameter_type = $type->getConduitParameterType();
 
       $parameter_type->setViewer($viewer);
 
       try {
         $value = $xaction['value'];
         $value = $parameter_type->getValue($xaction, 'value', $is_strict);
         $value = $type->getTransactionValueFromConduit($value);
         $xaction['value'] = $value;
       } catch (Exception $ex) {
         throw new PhutilProxyException(
           pht(
             'Exception when processing transaction of type "%s": %s',
             $xaction['type'],
             $ex->getMessage()),
           $ex);
       }
 
       $type_xactions = $type->generateTransactions(
         clone $template,
         $xaction);
 
       foreach ($type_xactions as $type_xaction) {
         $results[] = $type_xaction;
       }
     }
 
     return $results;
   }
 
 
   /**
    * @return map<string, PhabricatorEditType>
    * @task conduit
    */
   private function getConduitEditTypesFromFields(array $fields) {
     $types = array();
     foreach ($fields as $field) {
       $field_types = $field->getConduitEditTypes();
 
       if ($field_types === null) {
         continue;
       }
 
       foreach ($field_types as $field_type) {
         $types[$field_type->getEditType()] = $field_type;
       }
     }
     return $types;
   }
 
   public function getConduitEditTypes() {
     $config = $this->loadDefaultConfiguration();
     if (!$config) {
       return array();
     }
 
     $object = $this->newEditableObjectForDocumentation();
     $fields = $this->buildEditFields($object);
     return $this->getConduitEditTypesFromFields($fields);
   }
 
   final public static function getAllEditEngines() {
     return id(new PhutilClassMapQuery())
       ->setAncestorClass(__CLASS__)
       ->setUniqueMethod('getEngineKey')
       ->execute();
   }
 
   final public static function getByKey(PhabricatorUser $viewer, $key) {
     return id(new PhabricatorEditEngineQuery())
       ->setViewer($viewer)
       ->withEngineKeys(array($key))
       ->executeOne();
   }
 
   public function getIcon() {
     $application = $this->getApplication();
     return $application->getIcon();
   }
 
   private function loadUsableConfigurationsForCreate() {
     $viewer = $this->getViewer();
 
     $configs = id(new PhabricatorEditEngineConfigurationQuery())
       ->setViewer($viewer)
       ->withEngineKeys(array($this->getEngineKey()))
       ->withIsDefault(true)
       ->withIsDisabled(false)
       ->execute();
 
     $configs = msort($configs, 'getCreateSortKey');
 
     // Attach this specific engine to configurations we load so they can access
     // any runtime configuration. For example, this allows us to generate the
     // correct "Create Form" buttons when editing forms, see T12301.
     foreach ($configs as $config) {
       $config->attachEngine($this);
     }
 
     return $configs;
   }
 
   protected function getValidationExceptionShortMessage(
     PhabricatorApplicationTransactionValidationException $ex,
     PhabricatorEditField $field) {
 
     $xaction_type = $field->getTransactionType();
     if ($xaction_type === null) {
       return null;
     }
 
     return $ex->getShortMessage($xaction_type);
   }
 
   protected function getCreateNewObjectPolicy() {
     return PhabricatorPolicies::POLICY_USER;
   }
 
   private function requireCreateCapability() {
     PhabricatorPolicyFilter::requireCapability(
       $this->getViewer(),
       $this,
       PhabricatorPolicyCapability::CAN_EDIT);
   }
 
   private function hasCreateCapability() {
     return PhabricatorPolicyFilter::hasCapability(
       $this->getViewer(),
       $this,
       PhabricatorPolicyCapability::CAN_EDIT);
   }
 
   public function isCommentAction() {
     return ($this->getEditAction() == 'comment');
   }
 
   public function getEditAction() {
     $controller = $this->getController();
     $request = $controller->getRequest();
     return $request->getURIData('editAction');
   }
 
   protected function newCommentActionGroups() {
     return array();
   }
 
   protected function newAutomaticCommentTransactions($object) {
     return array();
   }
 
   protected function newCommentPreviewContent($object, array $xactions) {
     return null;
   }
 
 
 /* -(  Form Pages  )--------------------------------------------------------- */
 
 
   public function getSelectedPage() {
     return $this->page;
   }
 
 
   private function selectPage($object, $page_key) {
     $pages = $this->getPages($object);
 
     if (empty($pages[$page_key])) {
       return null;
     }
 
     $this->page = $pages[$page_key];
     return $this->page;
   }
 
 
   protected function newPages($object) {
     return array();
   }
 
 
   protected function getPages($object) {
     if ($this->pages === null) {
       $pages = $this->newPages($object);
 
       assert_instances_of($pages, 'PhabricatorEditPage');
       $pages = mpull($pages, null, 'getKey');
 
       $this->pages = $pages;
     }
 
     return $this->pages;
   }
 
   private function applyPageToFields($object, array $fields) {
     $pages = $this->getPages($object);
     if (!$pages) {
       return $fields;
     }
 
     if (!$this->getSelectedPage()) {
       return $fields;
     }
 
     $page_picks = array();
     $default_key = head($pages)->getKey();
     foreach ($pages as $page_key => $page) {
       foreach ($page->getFieldKeys() as $field_key) {
         $page_picks[$field_key] = $page_key;
       }
       if ($page->getIsDefault()) {
         $default_key = $page_key;
       }
     }
 
     $page_map = array_fill_keys(array_keys($pages), array());
     foreach ($fields as $field_key => $field) {
       if (isset($page_picks[$field_key])) {
         $page_map[$page_picks[$field_key]][$field_key] = $field;
         continue;
       }
 
       // TODO: Maybe let the field pick a page to associate itself with so
       // extensions can force themselves onto a particular page?
 
       $page_map[$default_key][$field_key] = $field;
     }
 
     $page = $this->getSelectedPage();
     if (!$page) {
       $page = head($pages);
     }
 
     $selected_key = $page->getKey();
     return $page_map[$selected_key];
   }
 
   protected function willApplyTransactions($object, array $xactions) {
     return $xactions;
   }
 
   protected function didApplyTransactions($object, array $xactions) {
     return;
   }
 
 
 /* -(  Bulk Edits  )--------------------------------------------------------- */
 
   final public function newBulkEditGroupMap() {
     $groups = $this->newBulkEditGroups();
 
     $map = array();
     foreach ($groups as $group) {
       $key = $group->getKey();
 
       if (isset($map[$key])) {
         throw new Exception(
           pht(
             'Two bulk edit groups have the same key ("%s"). Each bulk edit '.
             'group must have a unique key.',
             $key));
       }
 
       $map[$key] = $group;
     }
 
     if ($this->isEngineExtensible()) {
       $extensions = PhabricatorEditEngineExtension::getAllEnabledExtensions();
     } else {
       $extensions = array();
     }
 
     foreach ($extensions as $extension) {
       $extension_groups = $extension->newBulkEditGroups($this);
       foreach ($extension_groups as $group) {
         $key = $group->getKey();
 
         if (isset($map[$key])) {
           throw new Exception(
             pht(
               'Extension "%s" defines a bulk edit group with the same key '.
               '("%s") as the main editor or another extension. Each bulk '.
               'edit group must have a unique key.',
               get_class($extension),
               $key));
         }
 
         $map[$key] = $group;
       }
     }
 
     return $map;
   }
 
   protected function newBulkEditGroups() {
     return array(
       id(new PhabricatorBulkEditGroup())
         ->setKey('default')
         ->setLabel(pht('Primary Fields')),
       id(new PhabricatorBulkEditGroup())
         ->setKey('extension')
         ->setLabel(pht('Support Applications')),
     );
   }
 
   final public function newBulkEditMap() {
     $viewer = $this->getViewer();
 
     $config = $this->loadDefaultConfiguration();
     if (!$config) {
       throw new Exception(
         pht('No default edit engine configuration for bulk edit.'));
     }
 
     $object = $this->newEditableObject();
     $fields = $this->buildEditFields($object);
     $groups = $this->newBulkEditGroupMap();
 
     $edit_types = $this->getBulkEditTypesFromFields($fields);
 
     $map = array();
     foreach ($edit_types as $key => $type) {
       $bulk_type = $type->getBulkParameterType();
       if ($bulk_type === null) {
         continue;
       }
 
       $bulk_type->setViewer($viewer);
 
       $bulk_label = $type->getBulkEditLabel();
       if ($bulk_label === null) {
         continue;
       }
 
       $group_key = $type->getBulkEditGroupKey();
       if (!$group_key) {
         $group_key = 'default';
       }
 
       if (!isset($groups[$group_key])) {
         throw new Exception(
           pht(
             'Field "%s" has a bulk edit group key ("%s") with no '.
             'corresponding bulk edit group.',
             $key,
             $group_key));
       }
 
       $map[] = array(
         'label' => $bulk_label,
         'xaction' => $key,
         'group' => $group_key,
         'control' => array(
           'type' => $bulk_type->getPHUIXControlType(),
           'spec' => (object)$bulk_type->getPHUIXControlSpecification(),
         ),
       );
     }
 
     return $map;
   }
 
 
   final public function newRawBulkTransactions(array $xactions) {
     $config = $this->loadDefaultConfiguration();
     if (!$config) {
       throw new Exception(
         pht('No default edit engine configuration for bulk edit.'));
     }
 
     $object = $this->newEditableObject();
     $fields = $this->buildEditFields($object);
 
     $edit_types = $this->getBulkEditTypesFromFields($fields);
     $template = $object->getApplicationTransactionTemplate();
 
     $raw_xactions = array();
     foreach ($xactions as $key => $xaction) {
       PhutilTypeSpec::checkMap(
         $xaction,
         array(
           'type' => 'string',
           'value' => 'optional wild',
         ));
 
       $type = $xaction['type'];
       if (!isset($edit_types[$type])) {
         throw new Exception(
           pht(
             'Unsupported bulk edit type "%s".',
             $type));
       }
 
       $edit_type = $edit_types[$type];
 
       // Replace the edit type with the underlying transaction type. Usually
       // these are 1:1 and the transaction type just has more internal noise,
       // but it's possible that this isn't the case.
       $xaction['type'] = $edit_type->getTransactionType();
 
       $value = $xaction['value'];
       $value = $edit_type->getTransactionValueFromBulkEdit($value);
       $xaction['value'] = $value;
 
       $xaction_objects = $edit_type->generateTransactions(
         clone $template,
         $xaction);
 
       foreach ($xaction_objects as $xaction_object) {
         $raw_xaction = array(
           'type' => $xaction_object->getTransactionType(),
           'metadata' => $xaction_object->getMetadata(),
           'new' => $xaction_object->getNewValue(),
         );
 
         if ($xaction_object->hasOldValue()) {
           $raw_xaction['old'] = $xaction_object->getOldValue();
         }
 
         if ($xaction_object->hasComment()) {
           $comment = $xaction_object->getComment();
           $raw_xaction['comment'] = $comment->getContent();
         }
 
         $raw_xactions[] = $raw_xaction;
       }
     }
 
     return $raw_xactions;
   }
 
   private function getBulkEditTypesFromFields(array $fields) {
     $types = array();
 
     foreach ($fields as $field) {
       $field_types = $field->getBulkEditTypes();
 
       if ($field_types === null) {
         continue;
       }
 
       foreach ($field_types as $field_type) {
         $types[$field_type->getEditType()] = $field_type;
       }
     }
 
     return $types;
   }
 
 
 /* -(  PhabricatorPolicyInterface  )----------------------------------------- */
 
 
   public function getPHID() {
     return get_class($this);
   }
 
   public function getCapabilities() {
     return array(
       PhabricatorPolicyCapability::CAN_VIEW,
       PhabricatorPolicyCapability::CAN_EDIT,
     );
   }
 
   public function getPolicy($capability) {
     switch ($capability) {
       case PhabricatorPolicyCapability::CAN_VIEW:
         return PhabricatorPolicies::getMostOpenPolicy();
       case PhabricatorPolicyCapability::CAN_EDIT:
         return $this->getCreateNewObjectPolicy();
     }
   }
 
   public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
     return false;
   }
 
 }
diff --git a/src/applications/transactions/view/PhabricatorApplicationTransactionCommentView.php b/src/applications/transactions/view/PhabricatorApplicationTransactionCommentView.php
index 8ec7ef0d8f..caf1ce5183 100644
--- a/src/applications/transactions/view/PhabricatorApplicationTransactionCommentView.php
+++ b/src/applications/transactions/view/PhabricatorApplicationTransactionCommentView.php
@@ -1,622 +1,635 @@
 <?php
 
 final class PhabricatorApplicationTransactionCommentView
   extends AphrontView {
 
   private $submitButtonName;
   private $action;
 
   private $previewPanelID;
   private $previewTimelineID;
   private $previewToggleID;
   private $formID;
   private $statusID;
   private $commentID;
   private $draft;
   private $requestURI;
   private $showPreview = true;
   private $objectPHID;
   private $headerText;
   private $noPermission;
   private $fullWidth;
   private $infoView;
   private $editEngineLock;
   private $noBorder;
   private $requiresMFA;
 
   private $currentVersion;
   private $versionedDraft;
   private $commentActions;
   private $commentActionGroups = array();
   private $transactionTimeline;
 
   public function setObjectPHID($object_phid) {
     $this->objectPHID = $object_phid;
     return $this;
   }
 
   public function getObjectPHID() {
     return $this->objectPHID;
   }
 
   public function setShowPreview($show_preview) {
     $this->showPreview = $show_preview;
     return $this;
   }
 
   public function getShowPreview() {
     return $this->showPreview;
   }
 
   public function setRequestURI(PhutilURI $request_uri) {
     $this->requestURI = $request_uri;
     return $this;
   }
   public function getRequestURI() {
     return $this->requestURI;
   }
 
   public function setCurrentVersion($current_version) {
     $this->currentVersion = $current_version;
     return $this;
   }
 
   public function getCurrentVersion() {
     return $this->currentVersion;
   }
 
   public function setVersionedDraft(
     PhabricatorVersionedDraft $versioned_draft) {
     $this->versionedDraft = $versioned_draft;
     return $this;
   }
 
   public function getVersionedDraft() {
     return $this->versionedDraft;
   }
 
   public function setDraft(PhabricatorDraft $draft) {
     $this->draft = $draft;
     return $this;
   }
 
   public function getDraft() {
     return $this->draft;
   }
 
   public function setSubmitButtonName($submit_button_name) {
     $this->submitButtonName = $submit_button_name;
     return $this;
   }
 
   public function getSubmitButtonName() {
     return $this->submitButtonName;
   }
 
   public function setAction($action) {
     $this->action = $action;
     return $this;
   }
 
   public function getAction() {
     return $this->action;
   }
 
   public function setHeaderText($text) {
     $this->headerText = $text;
     return $this;
   }
 
   public function setFullWidth($fw) {
     $this->fullWidth = $fw;
     return $this;
   }
 
   public function setInfoView(PHUIInfoView $info_view) {
     $this->infoView = $info_view;
     return $this;
   }
 
   public function getInfoView() {
     return $this->infoView;
   }
 
   public function setCommentActions(array $comment_actions) {
     assert_instances_of($comment_actions, 'PhabricatorEditEngineCommentAction');
     $this->commentActions = $comment_actions;
     return $this;
   }
 
   public function getCommentActions() {
     return $this->commentActions;
   }
 
   public function setCommentActionGroups(array $groups) {
     assert_instances_of($groups, 'PhabricatorEditEngineCommentActionGroup');
     $this->commentActionGroups = $groups;
     return $this;
   }
 
   public function getCommentActionGroups() {
     return $this->commentActionGroups;
   }
 
   public function setNoPermission($no_permission) {
     $this->noPermission = $no_permission;
     return $this;
   }
 
   public function getNoPermission() {
     return $this->noPermission;
   }
 
   public function setEditEngineLock(PhabricatorEditEngineLock $lock) {
     $this->editEngineLock = $lock;
     return $this;
   }
 
   public function getEditEngineLock() {
     return $this->editEngineLock;
   }
 
   public function setRequiresMFA($requires_mfa) {
     $this->requiresMFA = $requires_mfa;
     return $this;
   }
 
   public function getRequiresMFA() {
     return $this->requiresMFA;
   }
 
   public function setTransactionTimeline(
     PhabricatorApplicationTransactionView $timeline) {
 
     $timeline->setQuoteTargetID($this->getCommentID());
     if ($this->getNoPermission() || $this->getEditEngineLock()) {
       $timeline->setShouldTerminate(true);
     }
 
     $this->transactionTimeline = $timeline;
     return $this;
   }
 
   public function render() {
     if ($this->getNoPermission()) {
       return null;
     }
 
     $lock = $this->getEditEngineLock();
     if ($lock) {
       return id(new PHUIInfoView())
         ->setSeverity(PHUIInfoView::SEVERITY_WARNING)
         ->setErrors(
           array(
             $lock->getLockedObjectDisplayText(),
           ));
     }
 
     $viewer = $this->getViewer();
     if (!$viewer->isLoggedIn()) {
       $uri = id(new PhutilURI('/login/'))
         ->replaceQueryParam('next', (string)$this->getRequestURI());
       return id(new PHUIObjectBoxView())
         ->setFlush(true)
         ->appendChild(
           javelin_tag(
             'a',
             array(
               'class' => 'login-to-comment button',
               'href' => $uri,
             ),
             pht('Log In to Comment')));
     }
 
     if ($this->getRequiresMFA()) {
       if (!$viewer->getIsEnrolledInMultiFactor()) {
         $viewer->updateMultiFactorEnrollment();
         if (!$viewer->getIsEnrolledInMultiFactor()) {
           $messages = array();
           $messages[] = pht(
             'You must provide multi-factor credentials to comment or make '.
             'changes, but you do not have multi-factor authentication '.
             'configured on your account.');
           $messages[] = pht(
             'To continue, configure multi-factor authentication in Settings.');
 
           return id(new PHUIInfoView())
             ->setSeverity(PHUIInfoView::SEVERITY_MFA)
             ->setErrors($messages);
         }
       }
     }
 
     $data = array();
 
     $comment = $this->renderCommentPanel();
 
     if ($this->getShowPreview()) {
       $preview = $this->renderPreviewPanel();
     } else {
       $preview = null;
     }
 
     if (!$this->getCommentActions()) {
       Javelin::initBehavior(
         'phabricator-transaction-comment-form',
         array(
           'formID'        => $this->getFormID(),
           'timelineID'    => $this->getPreviewTimelineID(),
           'panelID'       => $this->getPreviewPanelID(),
           'showPreview'   => $this->getShowPreview(),
           'actionURI'     => $this->getAction(),
         ));
     }
 
     require_celerity_resource('phui-comment-form-css');
     $image_uri = $viewer->getProfileImageURI();
     $image = javelin_tag(
       'div',
       array(
         'style' => 'background-image: url('.$image_uri.')',
         'class' => 'phui-comment-image',
         'aural' => false,
       ));
     $wedge = phutil_tag(
       'div',
       array(
         'class' => 'phui-timeline-wedge',
       ),
       '');
 
     $badge_view = $this->renderBadgeView();
 
     $anchor = id(new PhabricatorAnchorView())
       ->setAnchorName('reply');
 
     $comment_box = id(new PHUIObjectBoxView())
       ->setFlush(true)
       ->addClass('phui-comment-form-view')
       ->addSigil('phui-comment-form')
       ->appendChild($anchor)
       ->appendChild(
         phutil_tag(
           'h3',
           array(
             'class' => 'aural-only',
           ),
           pht('Add Comment')))
       ->appendChild($image)
       ->appendChild($badge_view)
       ->appendChild($wedge)
       ->appendChild($comment);
 
     return array($comment_box, $preview);
   }
 
   private function renderCommentPanel() {
+    $viewer = $this->getViewer();
+
+    $remarkup_control = id(new PhabricatorRemarkupControl())
+      ->setViewer($viewer)
+      ->setID($this->getCommentID())
+      ->addClass('phui-comment-fullwidth-control')
+      ->addClass('phui-comment-textarea-control')
+      ->setCanPin(true)
+      ->setName('comment');
+
     $draft_comment = '';
+    $draft_metadata = array();
     $draft_key = null;
-    if ($this->getDraft()) {
-      $draft_comment = $this->getDraft()->getDraft();
-      $draft_key = $this->getDraft()->getDraftKey();
+
+    $legacy_draft = $this->getDraft();
+    if ($legacy_draft) {
+      $draft_comment = $legacy_draft->getDraft();
+      $draft_key = $legacy_draft->getDraftKey();
     }
 
     $versioned_draft = $this->getVersionedDraft();
     if ($versioned_draft) {
-      $draft_comment = $versioned_draft->getProperty('comment', '');
+      $draft_comment = $versioned_draft->getProperty(
+        'comment',
+        $draft_comment);
+      $draft_metadata = $versioned_draft->getProperty(
+        'metadata',
+        $draft_metadata);
     }
 
+    $remarkup_control->setValue($draft_comment);
+    $remarkup_control->setRemarkupMetadata($draft_metadata);
+
     if (!$this->getObjectPHID()) {
       throw new PhutilInvalidStateException('setObjectPHID', 'render');
     }
 
     $version_key = PhabricatorVersionedDraft::KEY_VERSION;
     $version_value = $this->getCurrentVersion();
 
     $form = id(new AphrontFormView())
-      ->setUser($this->getUser())
+      ->setUser($viewer)
       ->addSigil('transaction-append')
       ->setWorkflow(true)
       ->setFullWidth($this->fullWidth)
       ->setMetadata(
         array(
           'objectPHID' => $this->getObjectPHID(),
         ))
       ->setAction($this->getAction())
       ->setID($this->getFormID())
       ->addHiddenInput('__draft__', $draft_key)
       ->addHiddenInput($version_key, $version_value);
 
     $comment_actions = $this->getCommentActions();
     if ($comment_actions) {
       $action_map = array();
       $type_map = array();
 
       $comment_actions = mpull($comment_actions, null, 'getKey');
 
       $draft_actions = array();
       $draft_keys = array();
       if ($versioned_draft) {
         $draft_actions = $versioned_draft->getProperty('actions', array());
 
         if (!is_array($draft_actions)) {
           $draft_actions = array();
         }
 
         foreach ($draft_actions as $action) {
           $type = idx($action, 'type');
           $comment_action = idx($comment_actions, $type);
           if (!$comment_action) {
             continue;
           }
 
           $value = idx($action, 'value');
           $comment_action->setValue($value);
 
           $draft_keys[] = $type;
         }
       }
 
       foreach ($comment_actions as $key => $comment_action) {
         $key = $comment_action->getKey();
         $label = $comment_action->getLabel();
 
         $action_map[$key] = array(
           'key' => $key,
           'label' => $label,
           'type' => $comment_action->getPHUIXControlType(),
           'spec' => $comment_action->getPHUIXControlSpecification(),
           'initialValue' => $comment_action->getInitialValue(),
           'groupKey' => $comment_action->getGroupKey(),
           'conflictKey' => $comment_action->getConflictKey(),
           'auralLabel' => pht('Remove Action: %s', $label),
           'buttonText' => $comment_action->getSubmitButtonText(),
         );
 
         $type_map[$key] = $comment_action;
       }
 
       $options = $this->newCommentActionOptions($action_map);
 
       $action_id = celerity_generate_unique_node_id();
       $input_id = celerity_generate_unique_node_id();
       $place_id = celerity_generate_unique_node_id();
 
       $form->appendChild(
         phutil_tag(
           'input',
           array(
             'type' => 'hidden',
             'name' => 'editengine.actions',
             'id' => $input_id,
           )));
 
       $invisi_bar = phutil_tag(
         'div',
         array(
           'id' => $place_id,
           'class' => 'phui-comment-control-stack',
         ));
 
       $action_select = id(new AphrontFormSelectControl())
         ->addClass('phui-comment-fullwidth-control')
         ->addClass('phui-comment-action-control')
         ->setID($action_id)
         ->setOptions($options);
 
       $action_bar = phutil_tag(
         'div',
         array(
           'class' => 'phui-comment-action-bar grouped',
         ),
         array(
           $action_select,
         ));
 
       $form->appendChild($action_bar);
 
       $info_view = $this->getInfoView();
       if ($info_view) {
         $form->appendChild($info_view);
       }
 
       if ($this->getRequiresMFA()) {
         $message = pht(
           'You will be required to provide multi-factor credentials to '.
           'comment or make changes.');
 
         $form->appendChild(
           id(new PHUIInfoView())
             ->setSeverity(PHUIInfoView::SEVERITY_MFA)
             ->setErrors(array($message)));
       }
 
       $form->appendChild($invisi_bar);
       $form->addClass('phui-comment-has-actions');
 
       $timeline = $this->transactionTimeline;
 
       $view_data = array();
       if ($timeline) {
         $view_data = $timeline->getViewData();
       }
 
       Javelin::initBehavior(
         'comment-actions',
         array(
           'actionID' => $action_id,
           'inputID' => $input_id,
           'formID' => $this->getFormID(),
           'placeID' => $place_id,
           'panelID' => $this->getPreviewPanelID(),
           'timelineID' => $this->getPreviewTimelineID(),
           'actions' => $action_map,
           'showPreview' => $this->getShowPreview(),
           'actionURI' => $this->getAction(),
           'drafts' => $draft_keys,
           'defaultButtonText' => $this->getSubmitButtonName(),
           'viewData' => $view_data,
         ));
     }
 
     $submit_button = id(new AphrontFormSubmitControl())
       ->addClass('phui-comment-fullwidth-control')
       ->addClass('phui-comment-submit-control')
       ->setValue($this->getSubmitButtonName());
 
     $form
-      ->appendChild(
-        id(new PhabricatorRemarkupControl())
-          ->setID($this->getCommentID())
-          ->addClass('phui-comment-fullwidth-control')
-          ->addClass('phui-comment-textarea-control')
-          ->setCanPin(true)
-          ->setName('comment')
-          ->setUser($this->getUser())
-          ->setValue($draft_comment))
+      ->appendChild($remarkup_control)
       ->appendChild(
         id(new AphrontFormSubmitControl())
           ->addClass('phui-comment-fullwidth-control')
           ->addClass('phui-comment-submit-control')
           ->addSigil('submit-transactions')
           ->setValue($this->getSubmitButtonName()));
 
     return $form;
   }
 
   private function renderPreviewPanel() {
 
     $preview = id(new PHUITimelineView())
       ->setID($this->getPreviewTimelineID());
 
     return phutil_tag(
       'div',
       array(
         'id'    => $this->getPreviewPanelID(),
         'style' => 'display: none',
         'class' => 'phui-comment-preview-view',
       ),
       $preview);
   }
 
   private function getPreviewPanelID() {
     if (!$this->previewPanelID) {
       $this->previewPanelID = celerity_generate_unique_node_id();
     }
     return $this->previewPanelID;
   }
 
   private function getPreviewTimelineID() {
     if (!$this->previewTimelineID) {
       $this->previewTimelineID = celerity_generate_unique_node_id();
     }
     return $this->previewTimelineID;
   }
 
   public function setFormID($id) {
     $this->formID = $id;
     return $this;
   }
 
   private function getFormID() {
     if (!$this->formID) {
       $this->formID = celerity_generate_unique_node_id();
     }
     return $this->formID;
   }
 
   private function getStatusID() {
     if (!$this->statusID) {
       $this->statusID = celerity_generate_unique_node_id();
     }
     return $this->statusID;
   }
 
   private function getCommentID() {
     if (!$this->commentID) {
       $this->commentID = celerity_generate_unique_node_id();
     }
     return $this->commentID;
   }
 
   private function newCommentActionOptions(array $action_map) {
     $options = array();
     $options['+'] = pht('Add Action...');
 
     // Merge options into groups.
     $groups = array();
     foreach ($action_map as $key => $item) {
       $group_key = $item['groupKey'];
       if (!isset($groups[$group_key])) {
         $groups[$group_key] = array();
       }
       $groups[$group_key][$key] = $item;
     }
 
     $group_specs = $this->getCommentActionGroups();
     $group_labels = mpull($group_specs, 'getLabel', 'getKey');
 
     // Reorder groups to put them in the same order as the recognized
     // group definitions.
     $groups = array_select_keys($groups, array_keys($group_labels)) + $groups;
 
     // Move options with no group to the end.
     $default_group = idx($groups, '');
     if ($default_group) {
       unset($groups['']);
       $groups[''] = $default_group;
     }
 
     foreach ($groups as $group_key => $group_items) {
       if (strlen($group_key)) {
         $group_label = idx($group_labels, $group_key, $group_key);
         $options[$group_label] = ipull($group_items, 'label');
       } else {
         foreach ($group_items as $key => $item) {
           $options[$key] = $item['label'];
         }
       }
     }
 
     return $options;
   }
 
   private function renderBadgeView() {
     $user = $this->getUser();
     $can_use_badges = PhabricatorApplication::isClassInstalledForViewer(
       'PhabricatorBadgesApplication',
       $user);
     if (!$can_use_badges) {
       return null;
     }
 
     // Pull Badges from UserCache
     $badges = $user->getRecentBadgeAwards();
     $badge_view = null;
     if ($badges) {
       $badge_list = array();
       foreach ($badges as $badge) {
         $badge_view = id(new PHUIBadgeMiniView())
           ->setIcon($badge['icon'])
           ->setQuality($badge['quality'])
           ->setHeader($badge['name'])
           ->setTipDirection('E')
           ->setHref('/badges/view/'.$badge['id'].'/');
 
         $badge_list[] = $badge_view;
       }
       $flex = new PHUIBadgeBoxView();
       $flex->addItems($badge_list);
       $flex->setCollapsed(true);
       $badge_view = phutil_tag(
         'div',
         array(
           'class' => 'phui-timeline-badges',
         ),
         $flex);
     }
 
     return $badge_view;
   }
 
 }
diff --git a/webroot/rsrc/js/core/behavior-drag-and-drop-textarea.js b/webroot/rsrc/js/core/behavior-drag-and-drop-textarea.js
index d46c21fa13..8cf14349b0 100644
--- a/webroot/rsrc/js/core/behavior-drag-and-drop-textarea.js
+++ b/webroot/rsrc/js/core/behavior-drag-and-drop-textarea.js
@@ -1,49 +1,59 @@
 /**
  * @provides javelin-behavior-aphront-drag-and-drop-textarea
  * @requires javelin-behavior
  *           javelin-dom
  *           javelin-json
  *           phabricator-drag-and-drop-file-upload
  *           phabricator-textareautils
  */
 
 JX.behavior('aphront-drag-and-drop-textarea', function(config) {
 
   var target = JX.$(config.target);
 
   var metadata_node = JX.$(config.remarkupMetadataID);
   var metadata_value = config.remarkupMetadataValue;
 
   function set_metadata(key, value) {
     metadata_value[key] = value;
-    metadata_node.value = JX.JSON.stringify(metadata_value);
+    write_metadata();
   }
 
   function get_metadata(key, default_value) {
     if (metadata_value.hasOwnProperty(key)) {
       return metadata_value[key];
     }
     return default_value;
   }
 
+  function write_metadata() {
+    metadata_node.value = JX.JSON.stringify(metadata_value);
+  }
+
+  write_metadata();
+
   if (JX.PhabricatorDragAndDropFileUpload.isSupported()) {
     var drop = new JX.PhabricatorDragAndDropFileUpload(target)
       .setURI(config.uri)
       .setChunkThreshold(config.chunkThreshold);
 
     drop.listen('didBeginDrag', function() {
       JX.DOM.alterClass(target, config.activatedClass, true);
     });
 
     drop.listen('didEndDrag', function() {
       JX.DOM.alterClass(target, config.activatedClass, false);
     });
 
     drop.listen('didUpload', function(file) {
       JX.TextAreaUtils.insertFileReference(target, file);
+
+      var phids = get_metadata('attachedFilePHIDs', []);
+      phids.push(file.getPHID());
+      set_metadata('attachedFilePHIDs', phids);
     });
 
     drop.start();
   }
 
 });