Two findings from the manual review on PR #1 that were intentionally deferred (option B scope) — neither is currently triggered, but both are real footguns worth closing.
1. extractCustomId returns '' for unknown modal shapes
src/utils/interactions/modalHelper.ts:80-86 probes .data.custom_id (ModalBuilder) and .custom_id (rawModal). If a future caller ever passes anything else, the helper returns '' and the awaitModalSubmit filter becomes i.customId === '' — never matches — so the call silently times out for the full TIMEOUTS.MODAL (5 minutes), notifies the user with a misleading timeout message, and blocks for that whole window.
No current caller triggers this (audited at review time) but the silent-fail mode is dangerous when the next modal helper lands. Fix: throw or warn-log when extractCustomId can't find a customId on either shape.
2. `Set as Default` does N sequential saves with shared-mutation race
src/commands/handlers/ticket/typeList.ts:179-186 iterates the in-memory types array and await typeRepo.save(t) per row in sequence. With 25 ticket types this is up to 25 sequential round-trips during which:
- The components are still rendered, so the user can click any other button on the same row.
- A concurrent collect handler reads from the same `types` array reference, sees the partially-mutated state mid-loop.
In practice both handlers converge because they both end up calling `save()` on the same object, but the in-place mutation while another handler holds a reference is brittle.
Fix: replace the loop with two typeRepo.update() calls — clear all (`{guildId}, {isDefault: false}`), then set one (`{guildId, typeId}, {isDefault: true}`). 2 round-trips instead of N, no shared-mutation window.
Priority
Low. Both are theoretical with current usage. Bundle into a future maintenance commit.
Two findings from the manual review on PR #1 that were intentionally deferred (option B scope) — neither is currently triggered, but both are real footguns worth closing.
1.
extractCustomIdreturns''for unknown modal shapessrc/utils/interactions/modalHelper.ts:80-86probes.data.custom_id(ModalBuilder) and.custom_id(rawModal). If a future caller ever passes anything else, the helper returns''and theawaitModalSubmitfilter becomesi.customId === ''— never matches — so the call silently times out for the fullTIMEOUTS.MODAL(5 minutes), notifies the user with a misleading timeout message, and blocks for that whole window.No current caller triggers this (audited at review time) but the silent-fail mode is dangerous when the next modal helper lands. Fix: throw or warn-log when
extractCustomIdcan't find a customId on either shape.2. `Set as Default` does N sequential saves with shared-mutation race
src/commands/handlers/ticket/typeList.ts:179-186iterates the in-memorytypesarray andawait typeRepo.save(t)per row in sequence. With 25 ticket types this is up to 25 sequential round-trips during which:In practice both handlers converge because they both end up calling `save()` on the same object, but the in-place mutation while another handler holds a reference is brittle.
Fix: replace the loop with two
typeRepo.update()calls — clear all (`{guildId}, {isDefault: false}`), then set one (`{guildId, typeId}, {isDefault: true}`). 2 round-trips instead of N, no shared-mutation window.Priority
Low. Both are theoretical with current usage. Bundle into a future maintenance commit.