The /escalate slash command never had a reason option in its definition
(commands/register.js only takes a 'level' option), so handleEscalate
hardcoded reason=null. The escalate button path passed null explicitly.
The log line wrote it verbatim as "Reason: null" on every escalate.
Remove the dead surface:
- runEscalation signature drops the reason parameter.
- The customer-facing email body drops the conditional reason suffix
(`reason ? `\n\nReason: ${reason}` : ''`) — always-false branch.
- The logging-channel post drops "\nReason: ${reason}".
- handleEscalate drops the `const reason = null;` line and the call-site arg.
- handleEscalateButton (handlers/buttons.js) drops the trailing `null` arg.
If we ever want to capture a reason, the slash command would need a
StringOption('reason') and an escalate-modal for the button path —
neither exists today.
The 1028-line handlers/commands.js bundled escalation logic + force-close
flow + /response tag CRUD + /panel + /signature + context-menu handlers +
several config-toggle slash commands. After the dispatch-table refactor it
was still a god module. Split into handlers/commands/ with one file per
topic; require('./commands') resolves to handlers/commands/index.js
(handlers/commands.js is removed).
Layout:
helpers.js — requireStaffRole, fetchLoggingChannel
(cross-submodule, kept here to avoid cycles with index.js)
escalation.js — runEscalation, runDeescalation, handleEscalate, handleDeescalate
(run* are still exported via index.js for handlers/buttons.js)
close.js — handleForceClose, handleCancelClose, handleCloseTimer
+ finalizeForceClose / postTranscript (timer callback)
response.js — handleResponse + send/create/edit/delete/list subcommands
+ handleAutocomplete (only /response autocompletes)
panel.js — handlePanel, buildPanelButtonRow, handleSignature
contextMenu.js — handleCreateTicketFromMessage, handleViewUserTickets
index.js — dispatch tables, handleCommand/handleContextMenu, plus the
short-and-not-thematic handlers (notifydm, add, remove,
transfer, move, topic, staffthread, pinmessages, gmailpoll,
help) and the public re-exports.
No behavior change — every imported name, every Discord call, every DB
write, every embed, every reply payload preserved verbatim. Public surface
of require('./commands') is still { handleCommand, handleContextMenu,
handleAutocomplete, runEscalation, runDeescalation }.
Largest single module is now index.js at 299 lines; others are 33–214.