Repository navigation
feat(reaction-roles): editable form, emoji picker, formatting, media, export/import - #1544
Conversation
PUT /api/guilds/:guildId/reaction-roles/:messageId edits an existing message: PATCHes the Discord message (embed + buttons) and reconciles mappings in a transaction; channel is derived from the stored message (immutable on edit); not-found maps to 404. createReactionRoleBody/updateReactionRoleBody gain an optional imageUrl that sets the embed image. buildButtonRows + assertSnowflakes helpers are shared by create and update (no duplication).
GET /api/guilds/:guildId/emojis returns the guild's custom emojis (id, name, animated) via the bot client when servable, else the Discord REST API. Feeds the reaction-role emoji picker's server-emoji tab.
Add nullable title/description/imageUrl columns to ReactionRoleMessage (+ migration) and write them on create/update, so the dashboard edit form can prefill the current embed content (roles already prefill from mappings).
Refactor the create dialog into a shared MessageForm with create/edit modes (Edit button on each message card; channel locked on edit). Add an auto-grow description with a Discord-markdown formatting toolbar (bold/italic/underline/ strikethrough/spoiler), a custom emoji picker (unicode + server custom emojis), an optional image-URL field with live preview, and a sticky Add-role header. All wired to the create/update/getEmojis API; matches the existing design system.
Export the guild's reaction-role messages to a re-importable JSON file (download + copy; ids omitted). Import validates pasted/uploaded JSON before any network call and bulk-creates sequentially, collecting per-item errors without aborting the batch.
Reaction-role create/edit optionally accept an uploaded image (multipart), forwarded to Discord as a message attachment (attachment://) with no persistent storage; the JSON/imageUrl path is unchanged. File size and mimetype are capped via the existing multer middleware.
MessageForm gains an image file picker (preview + clear) alongside the URL field; a selected file takes precedence and is sent as multipart to the create/update endpoints (the API client switches to FormData when a File is given, JSON otherwise).
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Failed to generate code suggestions for PR |
|
|
Caution Review failedPull request was closed or merged during review 📝 WalkthroughWalkthroughAdds full edit support for reaction-role dashboard messages, image upload via multipart form data, embed fields ( ChangesReaction Roles Dashboard Enhancement
Sequence Diagram(s)sequenceDiagram
participant User
participant ReactionRolesPage
participant MessageForm
participant ReactionRolesApi
participant RolesRoute
participant ReactionRolesService
participant DiscordApi
participant PrismaDb
User->>ReactionRolesPage: Click Edit on message
ReactionRolesPage->>MessageForm: mode=edit, initialMessage
MessageForm->>MessageForm: pre-fill fields
User->>MessageForm: Select image file
User->>MessageForm: Click Update
MessageForm->>ReactionRolesApi: update(guildId, messageId, payload, imageFile)
ReactionRolesApi->>RolesRoute: PUT multipart FormData
RolesRoute->>ReactionRolesService: updateReactionRoleMessage(options)
ReactionRolesService->>DiscordApi: PATCH /channels/{id}/messages/{id}
DiscordApi-->>ReactionRolesService: updated message
ReactionRolesService->>PrismaDb: $transaction(delete mappings, update message + imageUrl)
PrismaDb-->>ReactionRolesService: committed
ReactionRolesService-->>RolesRoute: { messageId }
RolesRoute-->>ReactionRolesApi: 200 response
ReactionRolesApi-->>MessageForm: success
MessageForm->>ReactionRolesPage: onSuccess
ReactionRolesPage->>ReactionRolesPage: refresh messages
sequenceDiagram
participant User
participant ImportDialog
participant ExportDeserializer
participant ReactionRolesApi
participant RolesRoute
User->>ImportDialog: Submit pasted JSON
ImportDialog->>ExportDeserializer: deserializeReactionRolesJSON(json)
alt valid payload list
loop each item
ImportDialog->>ReactionRolesApi: create(guildId, item)
ReactionRolesApi->>RolesRoute: POST reaction role
RolesRoute-->>ReactionRolesApi: success or error
ReactionRolesApi-->>ImportDialog: per-item result
end
else invalid payload
ExportDeserializer-->>ImportDialog: validation errors
end
ImportDialog-->>User: progress and result summary
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related issues
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Size Change: +5.04 kB (+1.12%) Total Size: 453 kB 📦 View Changed
ℹ️ View Unchanged
|
There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
packages/shared/src/services/ReactionRolesService/index.ts (1)
275-277: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winValidate Discord create response
messageIdbefore DB write/cleanup paths.Line 275 trusts
resp.json().idwithout checking shape/snowflake format. A malformed success payload can cascade into Prisma failure and a DELETE call using an invalid message path.Suggested fix
- const discordMessage = (await resp.json()) as { id: string } - const messageId = discordMessage.id + const discordMessage = (await resp.json()) as { id?: unknown } + if ( + typeof discordMessage.id !== 'string' || + !/^\d{17,20}$/.test(discordMessage.id) + ) { + throw new Error( + 'Discord API returned an invalid message ID', + ) + } + const messageId = discordMessage.id🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/shared/src/services/ReactionRolesService/index.ts` around lines 275 - 277, Add validation after extracting the messageId from the Discord response to ensure the response has the expected shape and that the messageId is in a valid Discord snowflake format before it is used in any database write operations or cleanup paths. Validate that discordMessage.id exists and matches the expected snowflake format (typically a numeric string), and throw an appropriate error if validation fails to prevent malformed data from reaching the Prisma operations and DELETE calls downstream.packages/backend/src/routes/roles.ts (1)
79-85: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winAdd write throttling to the multipart create endpoint.
Line 84 enables up to 8MB in-memory uploads on POST without
writeLimiter(PUT has it). This makes authenticated abuse much easier and can pressure process memory.Suggested fix
app.post( '/api/guilds/:guildId/reaction-roles', requireAuth, + writeLimiter, requireGuildModuleAccess('overview', 'manage'), validateParams(s.guildIdParam), imageUploadHandler,🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/backend/src/routes/roles.ts` around lines 79 - 85, The POST endpoint for `/api/guilds/:guildId/reaction-roles` is missing write throttling protection on multipart uploads. Add the `writeLimiter` middleware to the asyncHandler chain in this POST endpoint (the one that accepts `imageUploadHandler`) to protect against memory exhaustion from authenticated upload abuse. This middleware should be placed in the same position as it appears on the corresponding PUT endpoint to ensure consistent rate limiting across both create and update operations.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/backend/src/routes/roles.ts`:
- Around line 211-218: The catch block in the roles.ts error handling is
incorrectly mapping all non-not-found exceptions to AppError.badRequest, which
incorrectly classifies upstream failures, API outages, and timeouts as client
errors. Instead of defaulting all remaining errors to badRequest, distinguish
between actual client/validation errors and upstream/internal failures. Rethrow
or map unexpected errors appropriately so that transient failures and external
service issues are represented as server errors (5xx) rather than client errors
(4xx), preserving observability and enabling proper retry behavior.
In `@packages/frontend/src/pages/ReactionRoles.test.tsx`:
- Around line 1212-1257: The test 'submitting create form with file calls
api.reactionRoles.create with File arg' validates file selection but never
performs the actual form submission or verifies the API call. After confirming
the file is displayed in the UI with the existing expectations, add a
fireEvent.click action to submit the form (likely targeting a submit or create
button), then add an expect assertion using toHaveBeenCalledWith to verify that
vi.mocked(api.reactionRoles.create) was invoked with the File object as an
argument.
In `@packages/frontend/src/pages/ReactionRoles.tsx`:
- Around line 299-309: The mapping transformation in the anonymous function
passed to initialMessage.mappings.map() does not normalize legacy numeric style
values before binding them to the form. If existing data contains numeric styles
like '1', '2', '3', or '4', they will not match the select options on line 706,
causing edit state inconsistency and potentially allowing non-canonical values
to be submitted. Add a normalization function that converts legacy numeric style
values ('1' through '4') to their corresponding canonical string equivalents
('Primary', 'Secondary', 'Success', 'Danger') and apply this normalization to
the m.style value when creating the style property in the newEntries
transformation.
- Around line 328-337: The resetForm() function does not clear the file input
value, which prevents re-selecting the same file from triggering the change
event. Add a line in resetForm() to clear fileInputRef.current.value by setting
it to an empty string. Additionally, the file selection handler (around line
343) creates new preview URLs without revoking the previous ones, causing memory
leaks. Before creating a new preview URL with URL.createObjectURL(), first check
if a previous preview URL exists and revoke it using URL.revokeObjectURL() to
properly clean up the object URL.
In `@packages/frontend/src/utils/reactionRolesExport.ts`:
- Around line 205-215: The deserializeReactionRolesJSON function currently
accepts emoji and imageUrl values without validating they are strings, allowing
non-string values to pass validation and defer errors to later API calls. Add
type validation before including emoji in the roles mapping and before assigning
imageUrl to the payload, ensuring both are strings when present. If either value
exists but is not a string type, return valid: false to properly fail validation
during import rather than deferring the error downstream.
In `@packages/shared/src/services/ReactionRolesService/index.ts`:
- Around line 206-210: The create path at line 206 validates guildId and
channelId using assertSnowflakes, but does not validate each roleId within the
roles array before passing them to buildButtonRows or persisting them in
mappings. Add validation for each roleId element in the roles array using
assertSnowflakes (or extract role IDs and validate them) before building the
button rows. Apply the same validation in the second location mentioned at lines
289-292 to ensure consistency across all code paths that process role IDs.
---
Outside diff comments:
In `@packages/backend/src/routes/roles.ts`:
- Around line 79-85: The POST endpoint for `/api/guilds/:guildId/reaction-roles`
is missing write throttling protection on multipart uploads. Add the
`writeLimiter` middleware to the asyncHandler chain in this POST endpoint (the
one that accepts `imageUploadHandler`) to protect against memory exhaustion from
authenticated upload abuse. This middleware should be placed in the same
position as it appears on the corresponding PUT endpoint to ensure consistent
rate limiting across both create and update operations.
In `@packages/shared/src/services/ReactionRolesService/index.ts`:
- Around line 275-277: Add validation after extracting the messageId from the
Discord response to ensure the response has the expected shape and that the
messageId is in a valid Discord snowflake format before it is used in any
database write operations or cleanup paths. Validate that discordMessage.id
exists and matches the expected snowflake format (typically a numeric string),
and throw an appropriate error if validation fails to prevent malformed data
from reaching the Prisma operations and DELETE calls downstream.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 148e886d-35df-4a58-a45f-370eab062028
📒 Files selected for processing (22)
packages/backend/src/routes/guilds.tspackages/backend/src/routes/roles.tspackages/backend/src/schemas/management.tspackages/backend/src/services/GuildService.tspackages/backend/tests/integration/routes/roles.test.tspackages/backend/tests/unit/services/GuildService.emojis.test.tspackages/frontend/src/components/reactionRoles/ImportDialog.tsxpackages/frontend/src/components/ui/AutoGrowTextarea.tsxpackages/frontend/src/components/ui/EmojiPicker.tsxpackages/frontend/src/components/ui/FormattingToolbar.tsxpackages/frontend/src/pages/ReactionRoles.test.tsxpackages/frontend/src/pages/ReactionRoles.tsxpackages/frontend/src/services/api.tspackages/frontend/src/services/reactionRolesApi.test.tspackages/frontend/src/services/reactionRolesApi.tspackages/frontend/src/types/guild.tspackages/frontend/src/utils/reactionRolesExport.test.tspackages/frontend/src/utils/reactionRolesExport.tspackages/shared/src/services/ReactionRolesService/index.spec.tspackages/shared/src/services/ReactionRolesService/index.tsprisma/migrations/20260623065023_add_reaction_role_embed_content/migration.sqlprisma/schema.prisma
📜 Review details
🔇 Additional comments (9)
packages/backend/src/routes/guilds.ts (1)
144-154: LGTM!packages/frontend/src/services/api.ts (1)
17-17: LGTM!Also applies to: 178-181
packages/backend/tests/unit/services/GuildService.emojis.test.ts (1)
1-189: LGTM!packages/frontend/src/services/reactionRolesApi.test.ts (1)
7-9: LGTM!Also applies to: 47-69, 88-124
packages/frontend/src/utils/reactionRolesExport.ts (1)
19-194: LGTM!Also applies to: 220-225
packages/frontend/src/utils/reactionRolesExport.test.ts (1)
1-355: LGTM!packages/frontend/src/pages/ReactionRoles.test.tsx (1)
823-1258: 📐 Maintainability & Code QualityTests at lines 823-1258 may lack proper scoped setup; verify structure and isolation.
This review comment claims tests are outside
describe('ReactionRoles'), preventing thebeforeEachfrom running and potentially causing mock state leakage between tests. However, I was unable to access the actual file structure to verify the placement of the closing describe block (claimed at line 821), the beforeEach content (lines 73-76), and whether these tests are truly at the top level.To confirm this issue, manually verify:
- Whether line 821 closes the
describe('ReactionRoles')block- Whether tests at lines 823-1258 are indeed outside any describe block
- Whether mock/spy state is shared across tests (run tests in different orders to detect flakiness)
If confirmed, wrap these tests in a new describe block with its own
beforeEachto ensure proper setup/teardown.packages/shared/src/services/ReactionRolesService/index.ts (1)
166-174: 📐 Maintainability & Code QualityTighten
parseEmojireturn type to remove impossiblenull.Line 168 declares
| null, but every branch returns an object. Keeping the union forces misleading null handling downstream.packages/backend/src/services/GuildService.ts (1)
669-678: 🎯 Functional CorrectnessVerify emoji cache strategy in
getGuildEmojisimplementation.The review raises a valid concern about discord.js cache behavior—the
guild.emojis.cachemay be cold or incomplete if theGUILD_EMOJIS_AND_STICKERSgateway intent is not enabled, or if the cache was not pre-populated. However, the full code context is needed to confirm:
- Whether a fallback mechanism exists and how it's triggered
- Whether the early return at line 673 actually skips the intended fallback
- Whether explicit
fetch()is necessary given the application's intent configurationThe suggested fix using
await guild.emojis.fetch()is a safe approach, but the actual necessity depends on the deployment intent configuration and whether the current fallback already handles cold caches.
There was a problem hiding this comment.
19 issues found across 22 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/frontend/src/components/ui/AutoGrowTextarea.tsx">
<violation number="1" location="packages/frontend/src/components/ui/AutoGrowTextarea.tsx:56">
P2: Callback refs break auto-grow behavior because internal ref is never assigned. The resize effect then gets `null` and skips height updates.</violation>
</file>
<file name="packages/backend/src/services/GuildService.ts">
<violation number="1" location="packages/backend/src/services/GuildService.ts:732">
P2: Emoji fetch failure is rethrown, causing endpoint 500 on transient Discord errors. This breaks graceful degradation used by other guild option endpoints.</violation>
</file>
<file name="packages/frontend/src/components/ui/EmojiPicker.tsx">
<violation number="1" location="packages/frontend/src/components/ui/EmojiPicker.tsx:139">
P2: Server emoji list can become stale after guild switch because fetch effect ignores `guildId`. This can attach wrong guild emoji IDs to new mappings.</violation>
</file>
<file name="packages/frontend/src/services/reactionRolesApi.ts">
<violation number="1" location="packages/frontend/src/services/reactionRolesApi.ts:18">
P2: Nullable embed fields are typed as optional-only strings, so API `null` values are not represented in TypeScript. This can cause unsafe string usage in callers that trust this type.</violation>
</file>
<file name="packages/backend/src/routes/roles.ts">
<violation number="1" location="packages/backend/src/routes/roles.ts:84">
P2: Create reaction-role upload route lacks write rate limiting for in-memory 8MB files. This enables avoidable memory-pressure abuse by authenticated clients.</violation>
<violation number="2" location="packages/backend/src/routes/roles.ts:93">
P3: Multipart payload parsing/validation logic is duplicated across POST and PUT handlers. Extract a shared helper to keep behavior consistent and reduce divergence bugs.</violation>
<violation number="3" location="packages/backend/src/routes/roles.ts:217">
P2: PUT handler collapses unexpected service failures into 400 Bad Request. This hides server/upstream failures and returns incorrect status semantics.</violation>
</file>
<file name="packages/backend/tests/integration/routes/roles.test.ts">
<violation number="1" location="packages/backend/tests/integration/routes/roles.test.ts:568">
P2: PUT test missing service-call assertion. The POST upload test verifies that `mockCreateReactionRole` was called with the correct `imageFile` shape (`filename: 'test-image.png'`), but the PUT upload test only checks HTTP status/body — it never asserts that `updateReactionRoleMessage` was called at all, let alone with the right file arguments. This gap means the test passes even if the route handler silently drops the file.</violation>
</file>
<file name="packages/frontend/src/utils/reactionRolesExport.ts">
<violation number="1" location="packages/frontend/src/utils/reactionRolesExport.ts:109">
P2: Deserializer rejects empty title/description while serializer emits empty strings for missing embed content, so some exported files cannot be re-imported.</violation>
<violation number="2" location="packages/frontend/src/utils/reactionRolesExport.ts:208">
P2: Optional `emoji` and `imageUrl` are not validated for string type before building the API payload.</violation>
</file>
<file name="packages/frontend/src/pages/ReactionRoles.tsx">
<violation number="1" location="packages/frontend/src/pages/ReactionRoles.tsx:298">
P3: Successful submit can leak the preview blob URL because reset clears state without revoking. Revoke before returning an empty preview URL.</violation>
<violation number="2" location="packages/frontend/src/pages/ReactionRoles.tsx:304">
P2: The mapping style is cast but not normalized. If existing data contains legacy numeric style values (e.g., `'1'`, `'2'`), the `|| 'Primary'` fallback won't trigger because those are truthy strings. The `<Select>` component will have no matching option, leaving the form in an inconsistent state. Normalize numeric styles to their string equivalents (e.g., `'1'` → `'Primary'`) before binding.</violation>
<violation number="3" location="packages/frontend/src/pages/ReactionRoles.tsx:343">
P3: Selecting a new image file leaks the previous preview blob URL. Revoke the previous `imageFilePreviewUrl` before assigning a new object URL.</violation>
</file>
<file name="packages/shared/src/services/ReactionRolesService/index.ts">
<violation number="1" location="packages/shared/src/services/ReactionRolesService/index.ts:206">
P1: Each `roles[].roleId` is not validated in the create path before being interpolated into `custom_id` and persisted to mappings. The update path validates roleIds via `assertSnowflakes`, but the create path skips this. Invalid roleIds could produce malformed Discord component IDs and corrupt stored mappings. Add roleId validation here for consistency with the update path.</violation>
<violation number="2" location="packages/shared/src/services/ReactionRolesService/index.ts:477">
P1: PATCH image updates use `attachments: []`, which removes the uploaded file and breaks `attachment://...` embed images.</violation>
<violation number="3" location="packages/shared/src/services/ReactionRolesService/index.ts:511">
P1: External PATCH happens before DB mapping transaction, so DB failure leaves Discord/UI state out of sync with stored mappings.</violation>
</file>
<file name="packages/frontend/src/pages/ReactionRoles.test.tsx">
<violation number="1" location="packages/frontend/src/pages/ReactionRoles.test.tsx:795">
P3: Test name says 'preview shown when valid URL entered' but only asserts the input value — no assertion for a preview image or preview container element.</violation>
<violation number="2" location="packages/frontend/src/pages/ReactionRoles.test.tsx:858">
P2: globalThis.URL.createObjectURL is mutated directly and never restored, risking cross-test pollution if other tests rely on the real implementation.</violation>
<violation number="3" location="packages/frontend/src/pages/ReactionRoles.test.tsx:1212">
P2: Test name claims it verifies `api.reactionRoles.create` is called with a File argument, but the test never clicks submit, never asserts `.toHaveBeenCalled()`, and never inspects the call arguments.</violation>
</file>
Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.
Re-trigger cubic
EmojiPicker was 35% covered, dropping the frontend global coverage thresholds below their floor in CI. Add 13 tests (unicode select, lazy server-emoji fetch + custom format, loading/empty/error states, img fallback, tab switch, click-outside) → EmojiPicker 100% lines / 97% branches; global gate green. Note: the component uses raw fetch(/api/guilds/:id/emojis), not the api client.
There was a problem hiding this comment.
1 issue found across 1 file (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/backend/src/services/GuildService.ts">
<violation number="1" location="packages/backend/src/services/GuildService.ts:732">
P2: Emoji fetch failure is rethrown, causing endpoint 500 on transient Discord errors. This breaks graceful degradation used by other guild option endpoints.</violation>
</file>
<file name="packages/frontend/src/components/ui/EmojiPicker.tsx">
<violation number="1" location="packages/frontend/src/components/ui/EmojiPicker.tsx:139">
P2: Server emoji list can become stale after guild switch because fetch effect ignores `guildId`. This can attach wrong guild emoji IDs to new mappings.</violation>
</file>
<file name="packages/backend/src/routes/roles.ts">
<violation number="1" location="packages/backend/src/routes/roles.ts:84">
P2: Create reaction-role upload route lacks write rate limiting for in-memory 8MB files. This enables avoidable memory-pressure abuse by authenticated clients.</violation>
<violation number="2" location="packages/backend/src/routes/roles.ts:93">
P3: Multipart payload parsing/validation logic is duplicated across POST and PUT handlers. Extract a shared helper to keep behavior consistent and reduce divergence bugs.</violation>
<violation number="3" location="packages/backend/src/routes/roles.ts:217">
P2: PUT handler collapses unexpected service failures into 400 Bad Request. This hides server/upstream failures and returns incorrect status semantics.</violation>
</file>
<file name="packages/backend/tests/integration/routes/roles.test.ts">
<violation number="1" location="packages/backend/tests/integration/routes/roles.test.ts:568">
P2: PUT test missing service-call assertion. The POST upload test verifies that `mockCreateReactionRole` was called with the correct `imageFile` shape (`filename: 'test-image.png'`), but the PUT upload test only checks HTTP status/body — it never asserts that `updateReactionRoleMessage` was called at all, let alone with the right file arguments. This gap means the test passes even if the route handler silently drops the file.</violation>
</file>
<file name="packages/frontend/src/utils/reactionRolesExport.ts">
<violation number="1" location="packages/frontend/src/utils/reactionRolesExport.ts:109">
P2: Deserializer rejects empty title/description while serializer emits empty strings for missing embed content, so some exported files cannot be re-imported.</violation>
<violation number="2" location="packages/frontend/src/utils/reactionRolesExport.ts:208">
P2: Optional `emoji` and `imageUrl` are not validated for string type before building the API payload.</violation>
</file>
<file name="packages/frontend/src/pages/ReactionRoles.tsx">
<violation number="1" location="packages/frontend/src/pages/ReactionRoles.tsx:298">
P3: Successful submit can leak the preview blob URL because reset clears state without revoking. Revoke before returning an empty preview URL.</violation>
<violation number="2" location="packages/frontend/src/pages/ReactionRoles.tsx:304">
P2: The mapping style is cast but not normalized. If existing data contains legacy numeric style values (e.g., `'1'`, `'2'`), the `|| 'Primary'` fallback won't trigger because those are truthy strings. The `<Select>` component will have no matching option, leaving the form in an inconsistent state. Normalize numeric styles to their string equivalents (e.g., `'1'` → `'Primary'`) before binding.</violation>
<violation number="3" location="packages/frontend/src/pages/ReactionRoles.tsx:343">
P3: Selecting a new image file leaks the previous preview blob URL. Revoke the previous `imageFilePreviewUrl` before assigning a new object URL.</violation>
</file>
<file name="packages/shared/src/services/ReactionRolesService/index.ts">
<violation number="1" location="packages/shared/src/services/ReactionRolesService/index.ts:206">
P1: Each `roles[].roleId` is not validated in the create path before being interpolated into `custom_id` and persisted to mappings. The update path validates roleIds via `assertSnowflakes`, but the create path skips this. Invalid roleIds could produce malformed Discord component IDs and corrupt stored mappings. Add roleId validation here for consistency with the update path.</violation>
<violation number="2" location="packages/shared/src/services/ReactionRolesService/index.ts:477">
P1: PATCH image updates use `attachments: []`, which removes the uploaded file and breaks `attachment://...` embed images.</violation>
<violation number="3" location="packages/shared/src/services/ReactionRolesService/index.ts:511">
P1: External PATCH happens before DB mapping transaction, so DB failure leaves Discord/UI state out of sync with stored mappings.</violation>
</file>
<file name="packages/frontend/src/pages/ReactionRoles.test.tsx">
<violation number="1" location="packages/frontend/src/pages/ReactionRoles.test.tsx:858">
P2: globalThis.URL.createObjectURL is mutated directly and never restored, risking cross-test pollution if other tests rely on the real implementation.</violation>
<violation number="2" location="packages/frontend/src/pages/ReactionRoles.test.tsx:1212">
P2: Test name claims it verifies `api.reactionRoles.create` is called with a File argument, but the test never clicks submit, never asserts `.toHaveBeenCalled()`, and never inspects the call arguments.</violation>
</file>
<file name="packages/frontend/src/components/ui/AutoGrowTextarea.tsx">
<violation number="1" location="packages/frontend/src/components/ui/AutoGrowTextarea.tsx:56">
P2: Callback refs break auto-grow behavior because internal ref is never assigned. The resize effect then gets `null` and skips height updates.</violation>
</file>
<file name="packages/frontend/src/services/reactionRolesApi.ts">
<violation number="1" location="packages/frontend/src/services/reactionRolesApi.ts:18">
P2: Nullable embed fields are typed as optional-only strings, so API `null` values are not represented in TypeScript. This can cause unsafe string usage in callers that trust this type.</violation>
</file>
<file name="packages/frontend/src/components/ui/EmojiPicker.test.tsx">
<violation number="1" location="packages/frontend/src/components/ui/EmojiPicker.test.tsx:14">
P2: Missing `vi.unstubAllGlobals()` in `afterEach`: `vi.restoreAllMocks()` does not clean up `vi.stubGlobal()` calls, so global `fetch` stubs leak across tests. 7/12 tests call `vi.stubGlobal('fetch', ...)` and none clean it up; subsequent non-stubbing tests run with a polluted global fetch.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
ImportDialog (file read, validate-before-network, success-cleanup, per-item errors) and FormattingToolbar (wrap/insert for all 5 marks, null-ref no-op) were under-covered, dragging the frontend global coverage thresholds. Both now ~100% lines.
Add create/edit submit-path tests (imageUrl vs file precedence, role emoji, create/update failure messages, empty-roles validation) — ReactionRoles.tsx branch coverage 58.7%->74.4%, restoring the frontend global coverage thresholds with margin (branches 79.24%->79.99%).
The 24 add-role clicks re-render the growing role list (heavy: each row has an emoji picker + selects), exceeding vitest's 5s default on CI. Give it 20s.
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
packages/frontend/src/pages/ReactionRoles.test.tsx (2)
853-860: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
URL.createObjectURLis overwritten and not restored to its original implementation.This mutates a global and can contaminate later tests. Use
vi.spyOn(URL, 'createObjectURL')/mockRestore()(and same forrevokeObjectURL) instead of direct reassignment.Also applies to: 879-880
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/frontend/src/pages/ReactionRoles.test.tsx` around lines 853 - 860, Replace the direct reassignment of globalThis.URL.createObjectURL and globalThis.URL.revokeObjectURL with vi.spyOn() calls instead. Use vi.spyOn(URL, 'createObjectURL').mockImplementation(createObjectURLMock) and vi.spyOn(URL, 'revokeObjectURL').mockImplementation(vi.fn()) to ensure proper mocking that can be automatically restored after the test, preventing global contamination of other tests.
821-823: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winTests after Line 823 are outside the
describeblock and skip shared setup.Everything after Line 823 runs without the
beforeEachat Line 73, so mock state/default list setup is inconsistent and order-dependent. Keep these tests inside the samedescribeor add an equivalent top-levelbeforeEach.Also applies to: 823-2268
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/frontend/src/pages/ReactionRoles.test.tsx` around lines 821 - 823, The closing brace at line 821 is prematurely closing the describe block, causing all tests from line 823 onwards (the 'export button is present and disabled when no messages' test and others through line 2268) to run outside the describe block and skip the beforeEach setup at line 73. Move the closing brace of the describe block to the very end of the test file after all tests complete, so all tests remain within the describe block and share the consistent beforeEach initialization of mock state and default list setup.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/frontend/src/components/ui/EmojiPicker.test.tsx`:
- Around line 13-18: In the EmojiPicker.test.tsx file, the afterEach hook
currently only calls vi.restoreAllMocks(), which does not clean up global stubs
created with vi.stubGlobal(). Since multiple tests use vi.stubGlobal('fetch',
...) at various locations throughout the test file, you need to add
vi.unstubAllGlobals() to the afterEach cleanup block to ensure fetch stubs are
properly cleaned up between tests. Alternatively, replace all
vi.stubGlobal('fetch', ...) calls with vi.spyOn(globalThis, 'fetch') so that
vi.restoreAllMocks() will handle the cleanup, but adding vi.unstubAllGlobals()
to the existing afterEach is the minimal fix required.
In `@packages/frontend/src/pages/ReactionRoles.test.tsx`:
- Around line 2105-2163: The test 'create with role emoji includes emoji in
payload' does not actually set an emoji or verify it is included in the payload.
Add emoji selection in the test by finding and clicking on an emoji input
element after setting the role label and before submitting, then update the
assertion in the waitFor block to check that payload.roles[0].emoji is defined
and matches the selected emoji value, rather than only checking roles.length >
0.
- Around line 1905-1932: The test "description text is trimmed before submit"
only verifies the initial empty state of descriptionInput but does not actually
test the trimming behavior. To fix this, after verifying the input element
exists, add steps to fill the descriptionInput with text that includes leading
and trailing whitespace, simulate submitting the form (by clicking the button
that triggers the create action), and then assert that the
api.reactionRoles.create mock was called with a payload where the description
value has been trimmed of that whitespace.
- Around line 1847-1880: The test function named 'form closes after successful
submit' currently only exercises the Cancel button path and does not perform an
actual form submission. Either rename the test to accurately reflect that it
tests form closure via cancellation (such as 'form closes after clicking
cancel'), or extend the test to actually fill out and submit the form before
asserting that it closes, which would require interacting with form fields and
clicking the submit/create button after the mocks are set up.
---
Outside diff comments:
In `@packages/frontend/src/pages/ReactionRoles.test.tsx`:
- Around line 853-860: Replace the direct reassignment of
globalThis.URL.createObjectURL and globalThis.URL.revokeObjectURL with
vi.spyOn() calls instead. Use vi.spyOn(URL,
'createObjectURL').mockImplementation(createObjectURLMock) and vi.spyOn(URL,
'revokeObjectURL').mockImplementation(vi.fn()) to ensure proper mocking that can
be automatically restored after the test, preventing global contamination of
other tests.
- Around line 821-823: The closing brace at line 821 is prematurely closing the
describe block, causing all tests from line 823 onwards (the 'export button is
present and disabled when no messages' test and others through line 2268) to run
outside the describe block and skip the beforeEach setup at line 73. Move the
closing brace of the describe block to the very end of the test file after all
tests complete, so all tests remain within the describe block and share the
consistent beforeEach initialization of mock state and default list setup.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: eeee874a-773c-40a1-a368-a4d14dc04cbb
📒 Files selected for processing (4)
packages/frontend/src/components/reactionRoles/ImportDialog.test.tsxpackages/frontend/src/components/ui/EmojiPicker.test.tsxpackages/frontend/src/components/ui/FormattingToolbar.test.tsxpackages/frontend/src/pages/ReactionRoles.test.tsx
✅ Files skipped from review due to trivial changes (1)
- packages/frontend/src/components/reactionRoles/ImportDialog.test.tsx
📜 Review details
⏰ Context from checks skipped due to timeout. (12)
- GitHub Check: Test — shared
- GitHub Check: Test — backend
- GitHub Check: Test — bot
- GitHub Check: Test — frontend
- GitHub Check: cubic · AI code reviewer
- GitHub Check: quality / SAST (CodeQL) (javascript-typescript)
- GitHub Check: quality / Lint (lint)
- GitHub Check: danger / danger
- GitHub Check: compressed-size
- GitHub Check: Build — backend
- GitHub Check: Build — bot
- GitHub Check: Build — frontend
🔇 Additional comments (1)
packages/frontend/src/pages/ReactionRoles.test.tsx (1)
1212-1258: Already flagged in a previous review: this test still does not submit the form or assertapi.reactionRoles.createcall arguments.
There was a problem hiding this comment.
0 issues found across 6 files (changes from recent commits).
Requires human review: Auto-approval blocked by 24 unresolved issues from previous reviews.
Re-trigger cubic
SonarCloud's SSRF taint analysis can't trace the shared assertSnowflakes helper or encodeURIComponent, so validate the guild/channel/message ids with an inline regex-throw immediately before each Discord fetch (getGuildEmojis + the create POST and update PATCH), then use the validated value directly.
There was a problem hiding this comment.
0 issues found across 2 files (changes from recent commits).
Requires human review: Auto-approval blocked by 24 unresolved issues from previous reviews.
Re-trigger cubic
Cap every multer dimension (files:1, fields, parts, fieldSize) alongside the 8MB fileSize so a hostile multipart request cannot exhaust memory. The upload is a reviewed-safe config, so suppress SonarCloud S5693 (content-length 'make sure') for the one route via sonar.issue.ignore.
There was a problem hiding this comment.
1 issue found across 2 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
Extract parseReactionRolePayload(req): unknown (used by the POST + PUT handlers) and type the upload middleware (NextFunction, err: unknown), clearing 22 eslint no-explicit-any / no-unsafe-* / no-unused-vars errors that failed the Lint gate.
|
Final review pass — remaining threads addressed or resolved with rationale:
|
Addressed: SSRF/S5693/lint fixed; roleId route-validated; threads resolved.
|
|
Caution Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted. Error details |
🤖 I have created a release *beep* *boop* --- <details><summary>2.22.0</summary> ## [2.22.0](v2.21.0...v2.22.0) (2026-06-23) ### Features * **dashboard:** refresh, single server switcher, i18n, avatar+cursor ([#1546](#1546)) ([abda321](abda321)) * **infra:** homelab staging environment for visual PR review ([#1547](#1547)) ([c15fa38](c15fa38)) * **reaction-roles:** editable form, emoji picker, formatting, media, export/import ([#1544](#1544)) ([ac5e0e9](ac5e0e9)) ### Bug Fixes * **backend:** harden role + reaction-role write-path error handling ([#1543](#1543)) ([18a36ba](18a36ba)) * **infra:** route staging via host port ([#1548](#1548)) ([fe5b153](fe5b153)) * **infra:** staging deploy git ownership ([#1554](#1554)) ([de5a322](de5a322)) * **infra:** staging deploy health check ([#1556](#1556)) ([fda6eea](fda6eea)) </details> --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Release 2.22.0 adds a refreshed dashboard and a revamped reaction-roles editor, plus a new homelab staging environment for visual PR review. It also improves backend error handling and stabilizes staging deploys. - **New Features** - Dashboard refresh: single server switcher, i18n, avatar + cursor. - Reaction-roles editor: emoji picker, formatting/media, import/export. - Homelab staging environment for visual PR review. - **Bug Fixes** - Hardened role and reaction-role write-path error handling. - Staging reliability: routing via host port, git ownership, and health checks. <sup>Written for commit ca8c234. Summary will update on new commits.</sup> <a href="https://cubic.dev/pr/LucasSantana-Dev/Lucky/pull/1560?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. -->
## What First-class **Role Groups** + a **composite "add styled role"** dashboard action: turn the 4-step "create role → style it → edit the RR message → add the button" flow into one action with a **dry-run preview**. A Role Group is a style template (color/hoist/mentionable/buttonStyle/emoji) attached 1:1 to a reaction-role message; new roles inherit it. Designed via `/brainstorming → /deep-research → /grill-with-docs` (6 doc-grounded skeptics) and built TDD across 6 phases. ADR: `decisions/2026-06-23-role-groups-composite-actions.md`. ## How it's built (commit per phase) | Phase | What | Tests | |---|---|---| | 0 | `RoleGroup` model + nullable `groupId` FK (`@@unique`, SetNull) + migration | verifier PASS | | 1 | `ReactionRolesService.addRoleToMessage` — **DB-first** append (insert mapping → PATCH Discord; no `deleteMany`) | 6 | | 2 | `RoleGroupService` — seed (modal/mode + divergence), composite apply, **role-only compensation**, dry-run, hex↔int | 21 | | 3 | routes `/api/guilds/:guildId/role-groups` (settings:manage), Zod, **AppError** 404/409/400 mapping | 22 | | 4 | frontend `roleGroupsApi` + `AddStyledRoleForm` (dry-run preview, double-submit guard, partial_success) | 18 | | 4b | wired into `ReactionRoles.tsx` (create-group + mount form) | 4 | ## Key design decisions (from the grill) - **DB-first + compensation** (defer full idempotency to v2): the legacy update path is Discord-first → orphaned buttons on DB failure; the new append path inverts that to "stale visuals, correct data" (sidesteps #1555). - `color` stored as **hex string** (repo convention), converted to int for Discord; `permissions:'0'` on created roles; new roles land at **position 0** (no privilege escalation → requester-hierarchy check dropped). - Limits enforced (Discord-doc-cited): 25 buttons, 250 roles, 80-char labels. ## Verification - **All suites green:** shared 1286 · backend 1200 · bot 2609 · frontend 876 · `type:check` + lint clean. -⚠️ **Frontend is component/wiring-test-verified only** — the dashboard is auth-gated, so **manual visual verification is required before merge** (same posture as #1544). ## Out of scope (v2+) Exclusivity / pick-one · multi-message groups · bot slash-command · select-menu UI · full `IdempotencyKey` state-machine · bulk add · full #1555 hardening of the legacy update path. ## Related issues surfaced #1555 (RR transactionality — v1 sidesteps via DB-first) · #1549 / #1550 / #1551 / #1553 (filed during the work, unrelated to this feature). <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Adds first-class Role Groups and a one‑click “Add Styled Role” that creates a role, applies a shared style, attaches it to a reaction‑role message, and adds the button with a dry‑run preview. Switches reaction‑role updates to a DB‑first append path with `partial_success` handling and localizes the new UI, including a Cancel action. - **New Features** - `RoleGroup` model (1:1 with a reaction‑role message) with template fields: color (hex `0xRRGGBB`), hoist, mentionable, `buttonStyle`, `defaultEmoji` (`prisma` migration). - Backend: `RoleGroupService` and routes under `/api/guilds/:guildId/role-groups` (create/list/get/update; `POST /:id/roles` supports `dryRun`; `DELETE /:id/roles/:roleId` to detach). Zod validation; `AppError.conflict` maps to 409. - Shared: `@lucky/shared/services/ReactionRolesService.addRoleToMessage` appends DB‑first, enforces limits/dup checks, validates `roleId` as a Discord snowflake before insert, returns `partial_success` on Discord PATCH failure. - Frontend: `roleGroupsApi` and `AddStyledRoleForm` with dry‑run preview and `partial_success` messaging; wired into Reaction Roles page with “Create role group” and “Add styled role”. `ReactionRoleMessage` now includes `groupId`. - **Bug Fixes** - Security: guild‑ownership checks on create/add to prevent cross‑guild mutations (IDOR). - Validation: `fromMessageId` accepts CUIDs; style tie now defaults to Primary; early `roleId` validation prevents invalid DB writes. - Concurrency: wrap create‑and‑link in a transaction with a conditional update to close the race on group creation. - Wiring/Packaging: use the singleton `roleGroupService` in routes; export `@lucky/shared/services/ReactionRolesService` subpath to fix runtime module resolution. - UX/i18n: translated new labels/messages and added a Cancel action to close the add‑role form. <sup>Written for commit abdb6c1. Summary will update on new commits.</sup> <a href="https://cubic.dev/pr/LucasSantana-Dev/Lucky/pull/1557?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added Role Groups for managing reaction-role messages with shared style templates. * Added “Add Styled Role” with preview-first flow (including dry-run planning). * Added role-group REST endpoints integrated into the frontend, including styled-role add and role detach. * **Bug Fixes** * Hardened guild ownership checks, schema validation, and conflict/ID handling for safer role-group creation/linking. * Improved tie-breaking/style seeding and improved retry/atomic behavior under concurrency. * Added clearer 409 conflict handling and better partial-success UI messaging. * **Tests** * Added integration and unit test coverage for role groups, styling/tie-break logic, and concurrency scenarios. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
:robot: I have created a release *beep* *boop*
---
<details><summary>2.33.0</summary>
##
[2.33.0](https://github.com/LucasSantana-Dev/Lucky/compare/v2.32.3...v2.33.0)
(2026-07-09)
### Features
* **autoplay:** add implicit-dislike-penalty signal
([#1374](https://github.com/LucasSantana-Dev/Lucky/issues/1374))
([593c0ad](https://github.com/LucasSantana-Dev/Lucky/commit/593c0ada5b732b4a48d63204c8e849c4b5057677))
* **autoplay:** add recency-decay signal for queue diversity
([#1376](https://github.com/LucasSantana-Dev/Lucky/issues/1376))
([b85e2a0](https://github.com/LucasSantana-Dev/Lucky/commit/b85e2a0f5d79efd04590d17db58faee5f5a50d38))
* **autoplay:** boost candidates for frequently replayed tracks
([#1370](https://github.com/LucasSantana-Dev/Lucky/issues/1370))
([215edea](https://github.com/LucasSantana-Dev/Lucky/commit/215edea22b8e99ab114f3450323a5f5de61ce6fb))
* **autoplay:** guild opt-out toggle for sertanejo veto
([#1087](https://github.com/LucasSantana-Dev/Lucky/issues/1087))
([#1373](https://github.com/LucasSantana-Dev/Lucky/issues/1373))
([6cb5588](https://github.com/LucasSantana-Dev/Lucky/commit/6cb55883f850aca68f4a6d5c2d5a3e5b492df8d5))
* **autoplay:** guild-scope implicit dislike for autoplay skips
([#1578](https://github.com/LucasSantana-Dev/Lucky/issues/1578))
([70b8596](https://github.com/LucasSantana-Dev/Lucky/commit/70b8596e7d4a0de5468e701f111c288aef9abe35))
* **autoplay:** hit@k eval harness for recommendation scoring
([#1577](https://github.com/LucasSantana-Dev/Lucky/issues/1577))
([2a0c6c4](https://github.com/LucasSantana-Dev/Lucky/commit/2a0c6c43f83fc5bf2f552549f3dfc832aeb8a95f))
* **autoplay:** instrument outcome eval to disambiguate
[#1275](https://github.com/LucasSantana-Dev/Lucky/issues/1275)
([#1491](https://github.com/LucasSantana-Dev/Lucky/issues/1491))
([1921ab5](https://github.com/LucasSantana-Dev/Lucky/commit/1921ab58ac8de1826fe73a931a02a9a5bf9c7541))
* **autoplay:** quick-wins batch — mood-cache clear, provider telemetry,
accept-rate
([#1090](https://github.com/LucasSantana-Dev/Lucky/issues/1090)
[#1083](https://github.com/LucasSantana-Dev/Lucky/issues/1083)
[#1086](https://github.com/LucasSantana-Dev/Lucky/issues/1086))
([#1102](https://github.com/LucasSantana-Dev/Lucky/issues/1102))
([7d7e4f5](https://github.com/LucasSantana-Dev/Lucky/commit/7d7e4f5451a67eac311c72270e9fe59c7aa4ff98))
* **backend:** add zod validation to artists and toggles routes
([#1189](https://github.com/LucasSantana-Dev/Lucky/issues/1189))
([#1334](https://github.com/LucasSantana-Dev/Lucky/issues/1334))
([b59fb35](https://github.com/LucasSantana-Dev/Lucky/commit/b59fb35244d9f1712d0730035c154ba471e34544))
* **backend:** dedup key for support-report intake
([#1319](https://github.com/LucasSantana-Dev/Lucky/issues/1319))
([#1328](https://github.com/LucasSantana-Dev/Lucky/issues/1328))
([4d95307](https://github.com/LucasSantana-Dev/Lucky/commit/4d9530762e37c97440e2e6a6957886ff41fcc1b6))
* **backend:** move session store from Redis to Postgres
([#1111](https://github.com/LucasSantana-Dev/Lucky/issues/1111))
([#1396](https://github.com/LucasSantana-Dev/Lucky/issues/1396))
([ff5e0b6](https://github.com/LucasSantana-Dev/Lucky/commit/ff5e0b684aa369a208a3e01d9c9a8c4cc53e4308))
* **backend:** read-only guild members/roles service endpoints
([#1691](https://github.com/LucasSantana-Dev/Lucky/issues/1691))
([fd04b6e](https://github.com/LucasSantana-Dev/Lucky/commit/fd04b6ef5f8a1609a468bb97f43213df488af8a8))
* **backend:** request-id correlation middleware for
[#1286](https://github.com/LucasSantana-Dev/Lucky/issues/1286)
([#1417](https://github.com/LucasSantana-Dev/Lucky/issues/1417))
([671ed4c](https://github.com/LucasSantana-Dev/Lucky/commit/671ed4c90471670f68346b493eade85d55a92ee9))
* **backend:** support intake + admin routes + staff notification
([#1241](https://github.com/LucasSantana-Dev/Lucky/issues/1241))
([280faeb](https://github.com/LucasSantana-Dev/Lucky/commit/280faeb983e02ae498960cb98b1ca10e7f44af2f))
* **backend:** wire moderation executor into
GuildAutomationExecutionService
([#1066](https://github.com/LucasSantana-Dev/Lucky/issues/1066))
([43ed8db](https://github.com/LucasSantana-Dev/Lucky/commit/43ed8db3906c826d8f01738fc5a49052c7f94862))
* **batch:** batch-operation framework + bulk-move-messages flagship
([#1564](https://github.com/LucasSantana-Dev/Lucky/issues/1564))
([9d11bf5](https://github.com/LucasSantana-Dev/Lucky/commit/9d11bf5d985dc9765b75eecb0bc798e2fe0c6443))
* **bot:** /vaga command builds job posts with auto-tagged roles
([#1682](https://github.com/LucasSantana-Dev/Lucky/issues/1682))
([963e457](https://github.com/LucasSantana-Dev/Lucky/commit/963e457af6402437b10138124052df524af37a99))
* **bot:** add RSS bridge service for Criativaria guides
([#1608](https://github.com/LucasSantana-Dev/Lucky/issues/1608))
([2807cad](https://github.com/LucasSantana-Dev/Lucky/commit/2807cada542424d3e4b15eaf76ec56d6b7250c8a))
* **bot:** add weekly community digest service
([#1609](https://github.com/LucasSantana-Dev/Lucky/issues/1609))
([2a653bf](https://github.com/LucasSantana-Dev/Lucky/commit/2a653bff32f3e5afa15cb73aae4d41e0476da859))
* **bot:** afk status with mention replies
([#1689](https://github.com/LucasSantana-Dev/Lucky/issues/1689))
([d8b5ba1](https://github.com/LucasSantana-Dev/Lucky/commit/d8b5ba101ea69033f18d9236481c9f8225867f08))
* **bot:** criativaria live twitch notification (poll every 2 min)
([#1613](https://github.com/LucasSantana-Dev/Lucky/issues/1613))
([e1d10b6](https://github.com/LucasSantana-Dev/Lucky/commit/e1d10b6efa7dd570867f4e678e9d0f6e30368bf7))
* **bot:** extend mod-log posting, fix twitch startup silence
([#1698](https://github.com/LucasSantana-Dev/Lucky/issues/1698))
([fb48065](https://github.com/LucasSantana-Dev/Lucky/commit/fb4806554220e604094aee1e27a498376ad566ff))
* **bot:** instrument serversetup criativaria invocations
([#1288](https://github.com/LucasSantana-Dev/Lucky/issues/1288))
([#1390](https://github.com/LucasSantana-Dev/Lucky/issues/1390))
([c37f021](https://github.com/LucasSantana-Dev/Lucky/commit/c37f021f3c28a13a6a58ed2529dcc5e3269892b1))
* **bot:** persistent giveaways with reaction entry
([#1690](https://github.com/LucasSantana-Dev/Lucky/issues/1690))
([b715af9](https://github.com/LucasSantana-Dev/Lucky/commit/b715af9df16ad016eaa7feda5f6e287d2b441933))
* **bot:** post moderation case embeds to the mod-log channel
([#1696](https://github.com/LucasSantana-Dev/Lucky/issues/1696))
([884da0f](https://github.com/LucasSantana-Dev/Lucky/commit/884da0fc5d8d689751c02ab4546911d11dc11fb8))
* **bot:** reminders with /remind and delivery scheduler
([#1686](https://github.com/LucasSantana-Dev/Lucky/issues/1686))
([1228290](https://github.com/LucasSantana-Dev/Lucky/commit/12282903a07538c75869c5eabb24f2823fb7411d))
* **bot:** smart custom commands via generic command-kind seam (ADR
2026-07-03)
([#1684](https://github.com/LucasSantana-Dev/Lucky/issues/1684))
([f0c446c](https://github.com/LucasSantana-Dev/Lucky/commit/f0c446c5dad1ab343b9a8df57f7b89ea3eaccaa2))
* **bot:** starboard seeding and one-time first-star dm
([#1685](https://github.com/LucasSantana-Dev/Lucky/issues/1685))
([e12e2e4](https://github.com/LucasSantana-Dev/Lucky/commit/e12e2e4e18a1d5fc256c1c83b818384c8366fe60))
* **bot:** surface support url + correlation id in command error embeds
([#1240](https://github.com/LucasSantana-Dev/Lucky/issues/1240))
([1cc004b](https://github.com/LucasSantana-Dev/Lucky/commit/1cc004bcd4d14a36dc7c6d31442c6264972cdc24))
* **bot:** utility join-onboarding message + in-bot growth adr
([#1506](https://github.com/LucasSantana-Dev/Lucky/issues/1506))
([0a23775](https://github.com/LucasSantana-Dev/Lucky/commit/0a23775cdb458479e0e1b23ad1e42679d6036d57))
* **dashboard:** add role groups management page
([#1678](https://github.com/LucasSantana-Dev/Lucky/issues/1678))
([bb8bc2c](https://github.com/LucasSantana-Dev/Lucky/commit/bb8bc2c9d2e2a0dd2cccd3ff690c16531a576016))
* **dashboard:** reaction roles create and delete
([c5c351c](https://github.com/LucasSantana-Dev/Lucky/commit/c5c351cddeb494cbcebce5448f96538bd8f97954))
* **dashboard:** refresh, single server switcher, i18n, avatar+cursor
([#1546](https://github.com/LucasSantana-Dev/Lucky/issues/1546))
([abda321](https://github.com/LucasSantana-Dev/Lucky/commit/abda3219eb4e5fdbb376b619cf3ab99f390fdefc))
* **db:** add check constraints on guild_settings bounds
([#1124](https://github.com/LucasSantana-Dev/Lucky/issues/1124))
([#1338](https://github.com/LucasSantana-Dev/Lucky/issues/1338))
([a7c7400](https://github.com/LucasSantana-Dev/Lucky/commit/a7c74007bbb0df43e45e88de52971e3858ee729a))
* **deploy:** SHA-pinned deploys + auto-rollback on health failure
([#1230](https://github.com/LucasSantana-Dev/Lucky/issues/1230))
([e24f128](https://github.com/LucasSantana-Dev/Lucky/commit/e24f1284d89edc9a8a72cb2135df580c8f7467a7))
* **frontend:** add music surface pages and components
([bb40e9c](https://github.com/LucasSantana-Dev/Lucky/commit/bb40e9c667a79bfd588b1fc13a61b55c457c2fd8))
* **frontend:** add ServerLogs + ServerSettings UI pages
([#965](https://github.com/LucasSantana-Dev/Lucky/issues/965))
([89961d3](https://github.com/LucasSantana-Dev/Lucky/commit/89961d324aed39001737e1f9873b7def200aaa65))
* growth surfaces — /invite, landing SEO + CTA, guild telemetry
([#1494](https://github.com/LucasSantana-Dev/Lucky/issues/1494))
([205876d](https://github.com/LucasSantana-Dev/Lucky/commit/205876d4ad9ba415840abc4207ca9ac98a99e7f4))
* guild integrations pack
([#1669](https://github.com/LucasSantana-Dev/Lucky/issues/1669))
([64a41a4](https://github.com/LucasSantana-Dev/Lucky/commit/64a41a48a1147b06ca18e36cbd5f9a4551321d23))
* **guild-automation:** wire AutoMessages executor into execution
service ([#906](https://github.com/LucasSantana-Dev/Lucky/issues/906))
([#950](https://github.com/LucasSantana-Dev/Lucky/issues/950))
([b5e444b](https://github.com/LucasSantana-Dev/Lucky/commit/b5e444b20dc836cf2fc29c6d70ccb67c7ac2ae9e))
* **infra:** homelab staging environment for visual PR review
([#1547](https://github.com/LucasSantana-Dev/Lucky/issues/1547))
([c15fa38](https://github.com/LucasSantana-Dev/Lucky/commit/c15fa388a61db9d1c3cfe880885f32d6020a629d))
* **levels:** show member display names on leaderboard, not raw ids
([eb0d700](https://github.com/LucasSantana-Dev/Lucky/commit/eb0d7009a0519bb6c00f6dcab2865c7c571779ce))
* **logs:** async context propagation, discord alerts, noise filtering
([#1510](https://github.com/LucasSantana-Dev/Lucky/issues/1510))
([a952c54](https://github.com/LucasSantana-Dev/Lucky/commit/a952c5400518f29c650abb286fada80caf34f2cd))
* **moderation:** move a message to another channel via right-click
([#1516](https://github.com/LucasSantana-Dev/Lucky/issues/1516))
([9822893](https://github.com/LucasSantana-Dev/Lucky/commit/98228930177479a078016c1c20e1ba43d393b7fd))
* **music:** add previous-track command end to end
([#1239](https://github.com/LucasSantana-Dev/Lucky/issues/1239))
([#1347](https://github.com/LucasSantana-Dev/Lucky/issues/1347))
([7771167](https://github.com/LucasSantana-Dev/Lucky/commit/7771167e7cc1534c561d6bad3cfbd2bcf85e261f))
* **observability:** alert on redis control publish failures
([#1401](https://github.com/LucasSantana-Dev/Lucky/issues/1401))
([6e46cd7](https://github.com/LucasSantana-Dev/Lucky/commit/6e46cd7ab193dedbff4a7b62ac0c6060489a4607))
* **observability:** capture escaping errors to Sentry at chokepoints
([#1229](https://github.com/LucasSantana-Dev/Lucky/issues/1229))
([9448de4](https://github.com/LucasSantana-Dev/Lucky/commit/9448de4b2a4c41145a3db443faff3df1e5dae1b1))
* **observability:** deploy markers, heartbeat, alerts (Layers 1-3)
([#1103](https://github.com/LucasSantana-Dev/Lucky/issues/1103))
([24568c0](https://github.com/LucasSantana-Dev/Lucky/commit/24568c02af1b802624d08f184fa64dcadcc413b3))
* **queue:** queueResolver telemetry pilot
([#1084](https://github.com/LucasSantana-Dev/Lucky/issues/1084))
([#1100](https://github.com/LucasSantana-Dev/Lucky/issues/1100))
([527609a](https://github.com/LucasSantana-Dev/Lucky/commit/527609a86a3f1b67bda3dbf8b62b371213dc77ff))
* **reaction-roles:** editable form, emoji picker, formatting, media,
export/import
([#1544](https://github.com/LucasSantana-Dev/Lucky/issues/1544))
([ac5e0e9](https://github.com/LucasSantana-Dev/Lucky/commit/ac5e0e988a27468eb75ace887795ed80c2d75b5f))
* **role-groups:** composite add-styled-role v1
([#1557](https://github.com/LucasSantana-Dev/Lucky/issues/1557))
([c869811](https://github.com/LucasSantana-Dev/Lucky/commit/c8698117666b066f4f7aa5ad5bc38602321e6cd7))
* **security:** add security headers + csp report-only
([#1283](https://github.com/LucasSantana-Dev/Lucky/issues/1283))
([#1315](https://github.com/LucasSantana-Dev/Lucky/issues/1315))
([7413a1c](https://github.com/LucasSantana-Dev/Lucky/commit/7413a1cebe733cd62f583ed197cd3bba50428e82))
* **security:** collect CSP violations via report-uri sink
([#1283](https://github.com/LucasSantana-Dev/Lucky/issues/1283))
([#1415](https://github.com/LucasSantana-Dev/Lucky/issues/1415))
([6f68aa3](https://github.com/LucasSantana-Dev/Lucky/commit/6f68aa31b8d9a9582e36de85c77e32d58d8adfd5))
* service announce endpoint with timing-safe key + channel allowlist
([#1681](https://github.com/LucasSantana-Dev/Lucky/issues/1681))
([3fbf022](https://github.com/LucasSantana-Dev/Lucky/commit/3fbf02256d8aed9fb4df067dcba7d87389317acb))
* **settings:** add Discord role management page (CRUD + bulk-delete)
([#1524](https://github.com/LucasSantana-Dev/Lucky/issues/1524))
([7db3ca2](https://github.com/LucasSantana-Dev/Lucky/commit/7db3ca240dba8906cb2247266c806c1a178c58fe))
* **shared:** add reactionroles executor (capture/diff/apply)
([142882c](https://github.com/LucasSantana-Dev/Lucky/commit/142882cbe8cc23e12c6c183abd965d25231e1d4c))
* **shared:** support report foundation
([#1223](https://github.com/LucasSantana-Dev/Lucky/issues/1223))
([#1228](https://github.com/LucasSantana-Dev/Lucky/issues/1228))
([77258bc](https://github.com/LucasSantana-Dev/Lucky/commit/77258bc47c26dd58978112882a2793944d0b78e4))
* skip-reason telemetry via emoji reactions on now-playing
([#1377](https://github.com/LucasSantana-Dev/Lucky/issues/1377))
([5b1959f](https://github.com/LucasSantana-Dev/Lucky/commit/5b1959fd96057c396baf17f9929e6a32f9737eed))
* **staging:** opt-in test bot for pre-merge live smoke
([#1692](https://github.com/LucasSantana-Dev/Lucky/issues/1692))
([c54eaba](https://github.com/LucasSantana-Dev/Lucky/commit/c54eabab1525d04297b6241eba7ea979826159b1))
* **twitch:** add stream.offline, channel.update and channel.raid
EventSub events
([#1531](https://github.com/LucasSantana-Dev/Lucky/issues/1531))
([b05a9cf](https://github.com/LucasSantana-Dev/Lucky/commit/b05a9cfeab0fb6e389a70171e5e2264ae8a7577f))
* **twitch:** follower and subscriber role sync
([#1509](https://github.com/LucasSantana-Dev/Lucky/issues/1509))
([d353bc0](https://github.com/LucasSantana-Dev/Lucky/commit/d353bc068614da2310bf50393a3b321842bb8044))
* **ui:** community pages — Starboard and Levels as connected components
([fafa2ac](https://github.com/LucasSantana-Dev/Lucky/commit/fafa2ac225ba6af8c903acfda8bf45abed865606))
* **web:** per-route seo metadata + sitemap, robots, og-image
([79a5f0d](https://github.com/LucasSantana-Dev/Lucky/commit/79a5f0d88e2e9e5102822e9ff34222b280e082c2)),
closes [#1131](https://github.com/LucasSantana-Dev/Lucky/issues/1131)
[#1132](https://github.com/LucasSantana-Dev/Lucky/issues/1132)
* **web:** public /support form + admin report view + error-state wiring
([#1245](https://github.com/LucasSantana-Dev/Lucky/issues/1245))
([19d855e](https://github.com/LucasSantana-Dev/Lucky/commit/19d855e50ce7332280a7ae3e91f9bf8650c5ea21))
### Bug Fixes
* add missing fetch timeouts to GuildService Discord API calls
([#1641](https://github.com/LucasSantana-Dev/Lucky/issues/1641))
([a8a57d5](https://github.com/LucasSantana-Dev/Lucky/commit/a8a57d5b780e900e0fa856804910ef8e3ed67d7a))
* add timeouts to unbounded external fetch calls
([#1333](https://github.com/LucasSantana-Dev/Lucky/issues/1333))
([38dde55](https://github.com/LucasSantana-Dev/Lucky/commit/38dde55fb8631185fed7e3e025d94362fef68e13))
* **api:** wrap automod + moderation settings responses as { settings }
([#1142](https://github.com/LucasSantana-Dev/Lucky/issues/1142))
([04893fd](https://github.com/LucasSantana-Dev/Lucky/commit/04893fd55ef570eca5e8a0682798639a9b1b40e7))
* auth loop between web dashboard and api subdomains
([572e320](https://github.com/LucasSantana-Dev/Lucky/commit/572e320ef803d195a96eb0f66ec42445f73a1931))
* **auth:** log session lookup failures in optional auth
([#1286](https://github.com/LucasSantana-Dev/Lucky/issues/1286))
([ff2b3ab](https://github.com/LucasSantana-Dev/Lucky/commit/ff2b3ab9f3f06dd3b81194f4e3e3f8a113f523f5))
* **automod:** remove dead warn/mute/kick/ban switch cases
([#1511](https://github.com/LucasSantana-Dev/Lucky/issues/1511))
([8ec8ed7](https://github.com/LucasSantana-Dev/Lucky/commit/8ec8ed76cbba1e30442fe17c9f15aa9ccf494dff))
* **autoplay:** capture skip rejections (symmetric completion threshold)
([#1276](https://github.com/LucasSantana-Dev/Lucky/issues/1276))
([c282414](https://github.com/LucasSantana-Dev/Lucky/commit/c282414346bf6c2247ed5d8a22c0c9bfcc5fdeb2))
* **autoplay:** key track start-time per track, not per guild
([#1275](https://github.com/LucasSantana-Dev/Lucky/issues/1275))
([#1483](https://github.com/LucasSantana-Dev/Lucky/issues/1483))
([0853a90](https://github.com/LucasSantana-Dev/Lucky/commit/0853a90412ed64c6e9cec7732311e5648cdb49f1))
* **autoplay:** prevent over-queueing; ensure evicted recs get terminal
events ([#1589](https://github.com/LucasSantana-Dev/Lucky/issues/1589))
([815d763](https://github.com/LucasSantana-Dev/Lucky/commit/815d763329d60580f8034232b606d7d0b2814684))
* **autoplay:** provenance-aware genre guards open the seed neighborhood
([#1272](https://github.com/LucasSantana-Dev/Lucky/issues/1272))
([405af1e](https://github.com/LucasSantana-Dev/Lucky/commit/405af1eae055f661a42310717c39adab6aa220a4))
* **autoplay:** weight popularity over name similarity in similar mode
([#1273](https://github.com/LucasSantana-Dev/Lucky/issues/1273))
([cb24a7e](https://github.com/LucasSantana-Dev/Lucky/commit/cb24a7e9c6993b72be780c72710c524459f591d6))
* **backend:** add validateparams to forums route guildid and slug
([#1602](https://github.com/LucasSantana-Dev/Lucky/issues/1602))
([a7102f4](https://github.com/LucasSantana-Dev/Lucky/commit/a7102f4c5f54405af37d631bfd45506b8cef4615))
* **backend:** assert required env vars at startup and fail fast
([#1169](https://github.com/LucasSantana-Dev/Lucky/issues/1169))
([107e235](https://github.com/LucasSantana-Dev/Lucky/commit/107e235d6b22d7097dd67980b5944ad1bc138c65))
* **backend:** bound pagination limit on leaderboard + starboard entries
([#1307](https://github.com/LucasSantana-Dev/Lucky/issues/1307))
([c2b5cbe](https://github.com/LucasSantana-Dev/Lucky/commit/c2b5cbe2beceb2dc5761f74fb63eda03790de5d7))
* **backend:** degrade gracefully on external fetch timeouts
([#1342](https://github.com/LucasSantana-Dev/Lucky/issues/1342))
([#1345](https://github.com/LucasSantana-Dev/Lucky/issues/1345))
([5de7b69](https://github.com/LucasSantana-Dev/Lucky/commit/5de7b694e7215fd979ef506f66cb7d1f91067249))
* **backend:** enforce discord snowflake validation on all guild routes
([#1172](https://github.com/LucasSantana-Dev/Lucky/issues/1172))
([ec25670](https://github.com/LucasSantana-Dev/Lucky/commit/ec25670ea76a571c96ad20fc4e7df72d9ecf8255))
* **backend:** guard timingsafeequal against length mismatch in lastfm
route ([#1719](https://github.com/LucasSantana-Dev/Lucky/issues/1719))
([6d58671](https://github.com/LucasSantana-Dev/Lucky/commit/6d586711c804be9f66199338330ca0746c4e8fe5))
* **backend:** harden role + reaction-role write-path error handling
([#1543](https://github.com/LucasSantana-Dev/Lucky/issues/1543))
([18a36ba](https://github.com/LucasSantana-Dev/Lucky/commit/18a36ba673ffaa6e5a0f1d35f28bcd62a1c9c64c))
* **backend:** log swallowed spotify search errors
([#1285](https://github.com/LucasSantana-Dev/Lucky/issues/1285))
([#1318](https://github.com/LucasSantana-Dev/Lucky/issues/1318))
([72a7431](https://github.com/LucasSantana-Dev/Lucky/commit/72a74317105f6ae6aedb817344f79bcf0a16b373))
* **backend:** propagate db errors from deleteReactionRoleMessage
([#1604](https://github.com/LucasSantana-Dev/Lucky/issues/1604))
([b36fad0](https://github.com/LucasSantana-Dev/Lucky/commit/b36fad097822dd8013b02e5fd21d2cb536bab317))
* **backend:** replayed named creates return existing row
([#1320](https://github.com/LucasSantana-Dev/Lucky/issues/1320))
([#1326](https://github.com/LucasSantana-Dev/Lucky/issues/1326))
([be2b30c](https://github.com/LucasSantana-Dev/Lucky/commit/be2b30cdb69ad8d94665eb8ae2afb93c325bd5ce))
* **backend:** restrict cors allowlist to first-party hosts
([#1247](https://github.com/LucasSantana-Dev/Lucky/issues/1247))
([6021120](https://github.com/LucasSantana-Dev/Lucky/commit/602112012a2e42b0a0cbb7e5d1d2aa4299d9d7c1))
* **backend:** validate guildId snowflake on all 18 music routes
([#1297](https://github.com/LucasSantana-Dev/Lucky/issues/1297))
([b93cfb5](https://github.com/LucasSantana-Dev/Lucky/commit/b93cfb565288a36bb9c0cbb36640eadb874505d6))
* **backend:** wrap Spotify routes in asyncHandler
([#1184](https://github.com/LucasSantana-Dev/Lucky/issues/1184))
([#1219](https://github.com/LucasSantana-Dev/Lucky/issues/1219))
([a3be33b](https://github.com/LucasSantana-Dev/Lucky/commit/a3be33bceca656ff76325b3a7a10e53e4af83058))
* **batch:** bullmq worker requires maxretriesperrequest null redis
([#1665](https://github.com/LucasSantana-Dev/Lucky/issues/1665))
([f5adffe](https://github.com/LucasSantana-Dev/Lucky/commit/f5adffe8f17df02362ac34d619ad35836706e614))
* **bot:** accurate reply when previous button has no history
([#1191](https://github.com/LucasSantana-Dev/Lucky/issues/1191))
([#1331](https://github.com/LucasSantana-Dev/Lucky/issues/1331))
([eb9b2ea](https://github.com/LucasSantana-Dev/Lucky/commit/eb9b2ea28b1f0581910f73833e09caec41f50f34))
* **bot:** bound all Spotify API fetches with an 8s abort deadline
([#1302](https://github.com/LucasSantana-Dev/Lucky/issues/1302))
([b283159](https://github.com/LucasSantana-Dev/Lucky/commit/b2831594ecc1d456d6f683e89a2b22a996b9beb7))
* **bot:** capture failed error-replies to Sentry in interaction handler
([#1175](https://github.com/LucasSantana-Dev/Lucky/issues/1175))
([45665c2](https://github.com/LucasSantana-Dev/Lucky/commit/45665c2aff006ab26c8c0d6a3e2658df36d66aba))
* **bot:** catch floating promises in setTimeout callbacks
([#1210](https://github.com/LucasSantana-Dev/Lucky/issues/1210))
([#1218](https://github.com/LucasSantana-Dev/Lucky/issues/1218))
([8e790f9](https://github.com/LucasSantana-Dev/Lucky/commit/8e790f95f321da6b6e32206c4d29600dffef9401))
* **bot:** catch resume errors in skip delayed play
([#1353](https://github.com/LucasSantana-Dev/Lucky/issues/1353))
([#1354](https://github.com/LucasSantana-Dev/Lucky/issues/1354))
([c2d2758](https://github.com/LucasSantana-Dev/Lucky/commit/c2d275872fbab168a1e7c603ca415ab3d3284c27))
* **bot:** catch settings fetch errors in idle disconnect scheduling
([#1361](https://github.com/LucasSantana-Dev/Lucky/issues/1361))
([61b82e4](https://github.com/LucasSantana-Dev/Lucky/commit/61b82e4ce2f6f016b22ed5b0f6b672a4da55f0cb))
* **bot:** clear presence rotation interval on shutdown
([#1171](https://github.com/LucasSantana-Dev/Lucky/issues/1171))
([c6d35f5](https://github.com/LucasSantana-Dev/Lucky/commit/c6d35f5dee0a520b31ca51e7d0ad51105c09ad92))
* **bot:** collect /vaga descricao via modal, not a single-line option
([#1701](https://github.com/LucasSantana-Dev/Lucky/issues/1701))
([ba20cd8](https://github.com/LucasSantana-Dev/Lucky/commit/ba20cd8f0857983e0f0765e16cf91722ba568e6c))
* **bot:** dead-man heartbeat + exit on fatal init failure
([#1656](https://github.com/LucasSantana-Dev/Lucky/issues/1656))
([e379f5f](https://github.com/LucasSantana-Dev/Lucky/commit/e379f5f8bd62cfa6173cd514f1c83b5ef2080c65))
* **bot:** expand SoundCloud short links before discord-player
resolution
([#1177](https://github.com/LucasSantana-Dev/Lucky/issues/1177))
([ff84610](https://github.com/LucasSantana-Dev/Lucky/commit/ff84610786cfffbd3c106d168aad475543e32d16))
* **bot:** extend graceful bot-perm guard to mgmt + automod
([#1502](https://github.com/LucasSantana-Dev/Lucky/issues/1502))
([1ee510d](https://github.com/LucasSantana-Dev/Lucky/commit/1ee510dd3773d7f55d5a0b7d7e2be7bc0e027c17))
* **bot:** graceful bot-permission guard + moderation pilot
([#1498](https://github.com/LucasSantana-Dev/Lucky/issues/1498))
([#1499](https://github.com/LucasSantana-Dev/Lucky/issues/1499))
([e2664ce](https://github.com/LucasSantana-Dev/Lucky/commit/e2664ce97b6d99a63521036d3d4b5dbd0a051878))
* **bot:** ground autoplay on seed similarity + genre-condition scoring
([#1268](https://github.com/LucasSantana-Dev/Lucky/issues/1268))
([aeacbc6](https://github.com/LucasSantana-Dev/Lucky/commit/aeacbc61ce2618159d56333c22943777febf2dba))
* **bot:** harden youtube extractor registration
([#1468](https://github.com/LucasSantana-Dev/Lucky/issues/1468))
([#1472](https://github.com/LucasSantana-Dev/Lucky/issues/1472))
([2e7f1bb](https://github.com/LucasSantana-Dev/Lucky/commit/2e7f1bbec46aab6d68d19aa07a4a503027d304bd))
* **bot:** healthcheck gateway readiness instead of redis tcp ping
([#1047](https://github.com/LucasSantana-Dev/Lucky/issues/1047))
([5ce2514](https://github.com/LucasSantana-Dev/Lucky/commit/5ce2514d5fe6b73b8f1c869a63544e7f02977cb1))
* **bot:** lastfm-similar score crushed ~100x by match/100
([#1269](https://github.com/LucasSantana-Dev/Lucky/issues/1269))
([f6bba72](https://github.com/LucasSantana-Dev/Lucky/commit/f6bba729f3943edfb2e370015d93dc4b6a58d83b))
* **bot:** process threadcreate regardless of newlycreated flag
([#1606](https://github.com/LucasSantana-Dev/Lucky/issues/1606))
([be08786](https://github.com/LucasSantana-Dev/Lucky/commit/be08786eeac2b1213a58f1487d7d5b5eba53686a))
* **bot:** queue summary position is milliseconds, not seconds
([#1202](https://github.com/LucasSantana-Dev/Lucky/issues/1202))
([#1330](https://github.com/LucasSantana-Dev/Lucky/issues/1330))
([efa9800](https://github.com/LucasSantana-Dev/Lucky/commit/efa98005cafc9e4cbacfcd8cc7f28b7bd5e26b6e))
* **bot:** reject timeout in session restore race instead of resolving
null ([#1170](https://github.com/LucasSantana-Dev/Lucky/issues/1170))
([2456fb9](https://github.com/LucasSantana-Dev/Lucky/commit/2456fb9bc38c291c870e244e42ebbc26d119acdd))
* **bot:** skip startup session restore into empty voice channel
([#1469](https://github.com/LucasSantana-Dev/Lucky/issues/1469))
([dbcc08c](https://github.com/LucasSantana-Dev/Lucky/commit/dbcc08ce511b0f19b1bc2c490c584113485e59b3))
* **bot:** startup session restore scans postgres, not redis
([#1119](https://github.com/LucasSantana-Dev/Lucky/issues/1119))
([2636ba1](https://github.com/LucasSantana-Dev/Lucky/commit/2636ba1f8b0e379f7c3068778055cb6e643a9b0d))
* **bot:** stop all schedulers/timers on shutdown
([#1197](https://github.com/LucasSantana-Dev/Lucky/issues/1197))
([#1205](https://github.com/LucasSantana-Dev/Lucky/issues/1205))
([7180579](https://github.com/LucasSantana-Dev/Lucky/commit/71805799de105dd1cf33e158c958857035d502cc))
* **bot:** tear down Discord client on initializer step failure
([#1180](https://github.com/LucasSantana-Dev/Lucky/issues/1180))
([d81ac68](https://github.com/LucasSantana-Dev/Lucky/commit/d81ac683a3235a1b3fe39fa39eccb7aa971ce52a))
* **bot:** thread real Client into endGiveaway
([#1383](https://github.com/LucasSantana-Dev/Lucky/issues/1383))
([#1388](https://github.com/LucasSantana-Dev/Lucky/issues/1388))
([d3b274a](https://github.com/LucasSantana-Dev/Lucky/commit/d3b274afa694ae8b35e3052ba8a366afee32d98f))
* **bot:** validate text-based channel before send in embed command
([#1253](https://github.com/LucasSantana-Dev/Lucky/issues/1253))
([5add32f](https://github.com/LucasSantana-Dev/Lucky/commit/5add32f7e4d88164b89fe73e7ea8c9dcaf17f909))
* **bot:** wire role exclusion enforcement + guildmembers intent
([#1668](https://github.com/LucasSantana-Dev/Lucky/issues/1668))
([e8145af](https://github.com/LucasSantana-Dev/Lucky/commit/e8145afe9f4765b927b077d3df471a2280087e14))
* **bot:** wire setupwebmusichandler at startup
([#1321](https://github.com/LucasSantana-Dev/Lucky/issues/1321))
([#1351](https://github.com/LucasSantana-Dev/Lucky/issues/1351))
([dc68e7e](https://github.com/LucasSantana-Dev/Lucky/commit/dc68e7efce26598161380c60bedb1a728a72748d))
* bound external calls — Discord-429 storm + Musical-Taste hang
([#1141](https://github.com/LucasSantana-Dev/Lucky/issues/1141))
([739d653](https://github.com/LucasSantana-Dev/Lucky/commit/739d653271a12b5a278d141545c3b5618f39f50c))
* bound music queue params, type rolegroup mapping, harden ci lint
([#1588](https://github.com/LucasSantana-Dev/Lucky/issues/1588))
([309d5d9](https://github.com/LucasSantana-Dev/Lucky/commit/309d5d9c9bbb04d2362fd0fe65e2db0f677169c1))
* **ci:** add bot to required containers and replace dead unhealthy grep
([#1054](https://github.com/LucasSantana-Dev/Lucky/issues/1054))
([b16109e](https://github.com/LucasSantana-Dev/Lucky/commit/b16109ee1de691d9d1bac69ade0168a9a69b0a75))
* **ci:** add figurinhas2026 to Vercel deploy watch
([#1063](https://github.com/LucasSantana-Dev/Lucky/issues/1063))
([b3f5e64](https://github.com/LucasSantana-Dev/Lucky/commit/b3f5e6465be5a77222ad9eccb109c2df7c169a94))
* **ci:** archive squash-merged release branch instead of failing FF
([#946](https://github.com/LucasSantana-Dev/Lucky/issues/946))
([111e860](https://github.com/LucasSantana-Dev/Lucky/commit/111e860a9efa24590ee89e975da4ab7886aaacaf))
* **ci:** cf pages deploy uses root lockfile (stops silent failure)
([#1673](https://github.com/LucasSantana-Dev/Lucky/issues/1673))
([8824fc3](https://github.com/LucasSantana-Dev/Lucky/commit/8824fc377e17bd4938b8af7d21050b2aa47b4982))
* **ci:** danger node 24 compatibility
([#1659](https://github.com/LucasSantana-Dev/Lucky/issues/1659))
([862426a](https://github.com/LucasSantana-Dev/Lucky/commit/862426a32f7d786644a02c8bde5a4c5d1e6bb6e8))
* **ci:** grant review-tools caller the scopes its reusables require
([#1424](https://github.com/LucasSantana-Dev/Lucky/issues/1424))
([c215675](https://github.com/LucasSantana-Dev/Lucky/commit/c2156759754ed65cfecb8f8fa9b25f5347522f5c))
* **ci:** hard-fail deploy on sustained 429 instead of silent oauth pass
([#1045](https://github.com/LucasSantana-Dev/Lucky/issues/1045))
([2a6b05c](https://github.com/LucasSantana-Dev/Lucky/commit/2a6b05ccaad42dbade7b4ae374e27f8661b9ac0b))
* **ci:** lockfile-hash BuildKit npm cache key to prevent esbuild
version mismatch
([#1016](https://github.com/LucasSantana-Dev/Lucky/issues/1016))
([1b3c258](https://github.com/LucasSantana-Dev/Lucky/commit/1b3c2584baf7a9361da89bf911267ca6876ff667))
* **ci:** lockfile-hash BuildKit npm cache key to prevent esbuild
version mismatch
([#1065](https://github.com/LucasSantana-Dev/Lucky/issues/1065))
([3579966](https://github.com/LucasSantana-Dev/Lucky/commit/35799666b5acc8f90b109eef03757912d192379a))
* **ci:** lockfile-hash BuildKit npm cache key to prevent esbuild
version mismatch
([#1067](https://github.com/LucasSantana-Dev/Lucky/issues/1067))
([7c5c447](https://github.com/LucasSantana-Dev/Lucky/commit/7c5c447ebb128b2ea2cb4bc717c3a3d1d8a56c6c))
* **ci:** lockfile-hash BuildKit npm cache key to prevent esbuild
version mismatch
([#1075](https://github.com/LucasSantana-Dev/Lucky/issues/1075))
([00ee269](https://github.com/LucasSantana-Dev/Lucky/commit/00ee26962d35622af8bcaeb7a84f79bddb7ce5d8))
* **ci:** lowercase image ref in yt-dlp smoke test
([e6267f5](https://github.com/LucasSantana-Dev/Lucky/commit/e6267f516dc50c417be6cbebdfe1d9b1358368c0))
* **ci:** lowercase image ref in yt-dlp smoke test
([ccb2855](https://github.com/LucasSantana-Dev/Lucky/commit/ccb2855745d1640c5a75a2aaebdc1fd61605578a))
* **ci:** make husky optional in prepare script to unblock docker builds
([#1060](https://github.com/LucasSantana-Dev/Lucky/issues/1060))
([9bf7c0e](https://github.com/LucasSantana-Dev/Lucky/commit/9bf7c0e91e671eb1347d37d1265d6a2beb376d68))
* **ci:** pin GitHub Actions to commit SHAs, scope secrets, harden
Renovate
([#1706](https://github.com/LucasSantana-Dev/Lucky/issues/1706))
([1239e28](https://github.com/LucasSantana-Dev/Lucky/commit/1239e286df0e222f4a0b39f328c622901a96f916))
* **ci:** post error commit status on deploy lock contention
([#1052](https://github.com/LucasSantana-Dev/Lucky/issues/1052))
([869d6f0](https://github.com/LucasSantana-Dev/Lucky/commit/869d6f03eae0f807befa1f712ec3a4e6c422aa9d))
* **ci:** quality/Lint green again — core rules off for bot/shared at
root lint
([#1364](https://github.com/LucasSantana-Dev/Lucky/issues/1364))
([#1365](https://github.com/LucasSantana-Dev/Lucky/issues/1365))
([cc2322f](https://github.com/LucasSantana-Dev/Lucky/commit/cc2322f0ed999ffe1aabd341b1371260ace718d5))
* **ci:** scope docker build cache per matrix service
([#1712](https://github.com/LucasSantana-Dev/Lucky/issues/1712))
([3ed9aae](https://github.com/LucasSantana-Dev/Lucky/commit/3ed9aaec6e93ec713144427d2e8290300e214c15))
* **ci:** skip docker-build validation for non-docker-relevant PRs
([#1711](https://github.com/LucasSantana-Dev/Lucky/issues/1711))
([5ea19ea](https://github.com/LucasSantana-Dev/Lucky/commit/5ea19eabf162c7ab82a1bd242979df8d8354156e))
* **ci:** surface async deploy failures via commit statuses
([#1046](https://github.com/LucasSantana-Dev/Lucky/issues/1046))
([b0838ac](https://github.com/LucasSantana-Dev/Lucky/commit/b0838aca3cd3f7d69024619c4eb37eecea337b7f))
* **ci:** use v-prefixed trivy-action tag
([#934](https://github.com/LucasSantana-Dev/Lucky/issues/934))
([ffae3cf](https://github.com/LucasSantana-Dev/Lucky/commit/ffae3cfbb0941c6ca16a8bf387511c811cd6ed82))
* **codeql:** resolve open codeql alerts
([0e0dfbe](https://github.com/LucasSantana-Dev/Lucky/commit/0e0dfbe5c027788dcc94953a67b52bbe93cc952b))
* **compose:** tag container logs so loki labels them by name
([#1476](https://github.com/LucasSantana-Dev/Lucky/issues/1476))
([4e956df](https://github.com/LucasSantana-Dev/Lucky/commit/4e956dfaad3ab88ea4d7d1f2db5874b3535f4cdd))
* **deploy:** derive require_running_containers from docker compose
([#1601](https://github.com/LucasSantana-Dev/Lucky/issues/1601))
([5715249](https://github.com/LucasSantana-Dev/Lucky/commit/571524924f2efe0cd7b5ab0d990631a86f220378))
* **deploy:** persist last-good across deploys (gitignore it)
([#1234](https://github.com/LucasSantana-Dev/Lucky/issues/1234))
([44f6487](https://github.com/LucasSantana-Dev/Lucky/commit/44f6487bf6fad06c4bcab3f688feb7f974696719))
* **deploy:** pin to short image tag; never build under a pinned tag
([#1232](https://github.com/LucasSantana-Dev/Lucky/issues/1232))
([d874d59](https://github.com/LucasSantana-Dev/Lucky/commit/d874d59bf8bb49588c08c51226975cc80b8827b3))
* **deploy:** probe nginx health on container port 8080 not 80
([#1236](https://github.com/LucasSantana-Dev/Lucky/issues/1236))
([918689d](https://github.com/LucasSantana-Dev/Lucky/commit/918689dc29c7bd7163caa5bec2b0336cb508fd43))
* **deploy:** read webhook hooks.json from live directory mount
([#1231](https://github.com/LucasSantana-Dev/Lucky/issues/1231))
([e559f42](https://github.com/LucasSantana-Dev/Lucky/commit/e559f4260bf115400ecde124b6406275e42fd888))
* **deploy:** record auto-rollback last-good from image commit-sha
([#1235](https://github.com/LucasSantana-Dev/Lucky/issues/1235))
([9cc21e7](https://github.com/LucasSantana-Dev/Lucky/commit/9cc21e76d49a8fb0f3ea74a73ce7dcf59f460861))
* **deploy:** ship prisma cli in production images + unify esbuild
([#1080](https://github.com/LucasSantana-Dev/Lucky/issues/1080))
([8e422b3](https://github.com/LucasSantana-Dev/Lucky/commit/8e422b391dd939f12abf9dea5ab2501cd5777968))
* **deploy:** verify + cache-correct the frontend so deploys actually
reach users
([#1576](https://github.com/LucasSantana-Dev/Lucky/issues/1576))
([9178576](https://github.com/LucasSantana-Dev/Lucky/commit/9178576c2c3d9b2743b5417f2516f6d9208cacd8))
* **deploy:** wire GITHUB_DEPLOY_STATUS_TOKEN into lucky-webhook
container
([#1597](https://github.com/LucasSantana-Dev/Lucky/issues/1597))
([ad4b7ed](https://github.com/LucasSantana-Dev/Lucky/commit/ad4b7ed32a66ab945ed610333a58131379b7cc08))
* **deps:** bump multer to 2.2.0 to fix high-severity dos advisory
([#1493](https://github.com/LucasSantana-Dev/Lucky/issues/1493))
([4d57ac8](https://github.com/LucasSantana-Dev/Lucky/commit/4d57ac87b0b016ffd8d79306c0705f1294c9a37a))
* **deps:** bump qs to 6.15.2 and hono to 4.12.25 (audit)
([#1295](https://github.com/LucasSantana-Dev/Lucky/issues/1295))
([ae5d949](https://github.com/LucasSantana-Dev/Lucky/commit/ae5d949c2f756eb554a24621c952cd8021b7a580))
* **deps:** pin piscina 4.9.3 for high-severity rce advisory
([#1504](https://github.com/LucasSantana-Dev/Lucky/issues/1504))
([10b68e6](https://github.com/LucasSantana-Dev/Lucky/commit/10b68e6597bae7c39286662293f1587780df0a30))
* **deps:** resolve npm audit vulnerabilities (1 critical + 5 moderate)
([#1708](https://github.com/LucasSantana-Dev/Lucky/issues/1708))
([e2b07bf](https://github.com/LucasSantana-Dev/Lucky/commit/e2b07bfece040e3f65bf0e62ff5a7565b93b670d))
* **docker:** add C toolchain to deps-production for opus source-build
fallback
([#1310](https://github.com/LucasSantana-Dev/Lucky/issues/1310))
([5ed7f11](https://github.com/LucasSantana-Dev/Lucky/commit/5ed7f11e0747eef6f303d1aa8f3f1fe8889eb351))
* **docker:** bump npm to patch bundled undici/tar CVEs in base image
([#1709](https://github.com/LucasSantana-Dev/Lucky/issues/1709))
([a635a0c](https://github.com/LucasSantana-Dev/Lucky/commit/a635a0c33c77ff99cd27369d3a8398ea51cc96de))
* **docker:** copy CHANGELOG.md into frontend build context
([#937](https://github.com/LucasSantana-Dev/Lucky/issues/937))
([44f302e](https://github.com/LucasSantana-Dev/Lucky/commit/44f302e3faf8fd448558660e964ac93aaf5ef88f))
* **download:** drop invalid --extract-flat flag from yt-dlp download
([#1488](https://github.com/LucasSantana-Dev/Lucky/issues/1488))
([9d29144](https://github.com/LucasSantana-Dev/Lucky/commit/9d291441d59ce7a01e717ad04f6844ac3d8b7947))
* **frontend:** default add-to-discord cta to public application id
([#1495](https://github.com/LucasSantana-Dev/Lucky/issues/1495))
([efda71e](https://github.com/LucasSantana-Dev/Lucky/commit/efda71e38c7152abb0d5fca2a708cbe960720ec4))
* **frontend:** fix CF Pages API routing and remove Vercel Analytics
([#1596](https://github.com/LucasSantana-Dev/Lucky/issues/1596))
([2902984](https://github.com/LucasSantana-Dev/Lucky/commit/2902984146f090eca210149eb84ea6e593c40747))
* **frontend:** flush moderation empty state (stray dark band)
([#1693](https://github.com/LucasSantana-Dev/Lucky/issues/1693))
([c64041a](https://github.com/LucasSantana-Dev/Lucky/commit/c64041a9e5aa25d411ad5115cfa0038d779a693b))
* **frontend:** healthcheck uses busybox wget, not bash /dev/tcp
([#1106](https://github.com/LucasSantana-Dev/Lucky/issues/1106))
([ec7a449](https://github.com/LucasSantana-Dev/Lucky/commit/ec7a449140eb53be135db321d0b9ca8274aafd30))
* guard unsafe external API response handling
([#1207](https://github.com/LucasSantana-Dev/Lucky/issues/1207))
([#1217](https://github.com/LucasSantana-Dev/Lucky/issues/1217))
([3b26b72](https://github.com/LucasSantana-Dev/Lucky/commit/3b26b7279312643523533d945ee0d2e85d7b9337))
* **help:** split large command categories across embed fields
([#1489](https://github.com/LucasSantana-Dev/Lucky/issues/1489))
([0fa5786](https://github.com/LucasSantana-Dev/Lucky/commit/0fa578646fe0671eda85eb6b36db076c6e0fafb1))
* **infra:** route staging via host port
([#1548](https://github.com/LucasSantana-Dev/Lucky/issues/1548))
([fe5b153](https://github.com/LucasSantana-Dev/Lucky/commit/fe5b153b2ebadbfaf20f67c95e964f3f06d443fe))
* **infra:** staging deploy git ownership
([#1554](https://github.com/LucasSantana-Dev/Lucky/issues/1554))
([de5a322](https://github.com/LucasSantana-Dev/Lucky/commit/de5a3220dac6bd517280405c69097eaca8885380))
* **infra:** staging deploy health check
([#1556](https://github.com/LucasSantana-Dev/Lucky/issues/1556))
([fda6eea](https://github.com/LucasSantana-Dev/Lucky/commit/fda6eea11e2da3f215538850445d4b9dcfd9e394))
* **logs:** serialize server logs with level, message and actor
([#1677](https://github.com/LucasSantana-Dev/Lucky/issues/1677))
([acd8fe3](https://github.com/LucasSantana-Dev/Lucky/commit/acd8fe31493931cd1cba5e93ed46d93bd35fe514))
* **middleware:** resolve guildAccess non-atomic session+context
staleness window
([5a64dd1](https://github.com/LucasSantana-Dev/Lucky/commit/5a64dd17bb1d9143c82eee49821c38e75a7f13ab))
* **moderation:** route context menus in the live event handler
([#1517](https://github.com/LucasSantana-Dev/Lucky/issues/1517))
([0232237](https://github.com/LucasSantana-Dev/Lucky/commit/0232237d9912dac9281b2e53902c4487542b98ce))
* **music:** re-target music guild FKs to discordId
([#1270](https://github.com/LucasSantana-Dev/Lucky/issues/1270))
([765d9d8](https://github.com/LucasSantana-Dev/Lucky/commit/765d9d81d2b3e776b24d70e8089056f010dd6351))
* **music:** surface youtube unavailability instead of generic errors
([#1146](https://github.com/LucasSantana-Dev/Lucky/issues/1146))
([0e66fe2](https://github.com/LucasSantana-Dev/Lucky/commit/0e66fe2e2f7f70dcdabb81e18a25cff0bdf32a50))
* **player:** warn not error on bridge exhaustion for unplayable tracks
([#1507](https://github.com/LucasSantana-Dev/Lucky/issues/1507))
([d7a4a58](https://github.com/LucasSantana-Dev/Lucky/commit/d7a4a5885ffa74fe5cbf22819df08a4dbc53e729))
* **play:** isolate post-play background ops
([#1085](https://github.com/LucasSantana-Dev/Lucky/issues/1085))
([#1101](https://github.com/LucasSantana-Dev/Lucky/issues/1101))
([ba994c4](https://github.com/LucasSantana-Dev/Lucky/commit/ba994c428253737826bbd10540112714cc0d4c6f))
* **reaction-roles:** PUT panel edit deletes mappings by cuid not
snowflake
([#1675](https://github.com/LucasSantana-Dev/Lucky/issues/1675))
([#1676](https://github.com/LucasSantana-Dev/Lucky/issues/1676))
([a51c70f](https://github.com/LucasSantana-Dev/Lucky/commit/a51c70f77dac207c5c76c8b1155f77a033a5e04b))
* **reaction-roles:** validate roleIds, fix update rollback, serialize
concurrent appends
([#1587](https://github.com/LucasSantana-Dev/Lucky/issues/1587))
([6873ac8](https://github.com/LucasSantana-Dev/Lucky/commit/6873ac8d398aa26f50d6a204d7d1de83557a402e))
* **release:** set group-pull-request-title-pattern so releases auto-tag
([#1521](https://github.com/LucasSantana-Dev/Lucky/issues/1521))
([b0e0f5f](https://github.com/LucasSantana-Dev/Lucky/commit/b0e0f5f40497c4be98135acb66b24a79e6763df2))
* **release:** set pull-request-title-pattern to include version
([#1514](https://github.com/LucasSantana-Dev/Lucky/issues/1514))
([cde12ec](https://github.com/LucasSantana-Dev/Lucky/commit/cde12ecc9881b1ca496fb0112e98d3bae2910c8e))
* **release:** tag-guard reconciles autorelease label
([#1561](https://github.com/LucasSantana-Dev/Lucky/issues/1561))
([#1583](https://github.com/LucasSantana-Dev/Lucky/issues/1583))
([6505f44](https://github.com/LucasSantana-Dev/Lucky/commit/6505f446b55fd2eb5ca5c87c5d105f2da975e28c))
* resolve discord 429 rate-limit storm and archived thread crash
([#1078](https://github.com/LucasSantana-Dev/Lucky/issues/1078))
([e79858e](https://github.com/LucasSantana-Dev/Lucky/commit/e79858ec7505b441bf538e7c38452476bd3f78f1))
* resolve Prettier syntax error in queueManipulation.spec.ts
([#985](https://github.com/LucasSantana-Dev/Lucky/issues/985))
([7cf4c83](https://github.com/LucasSantana-Dev/Lucky/commit/7cf4c83ee45da7ade4559957ff7a707a3b15871a))
* **schema:** add unique guild+thread constraint to GuildForumThread
([#1607](https://github.com/LucasSantana-Dev/Lucky/issues/1607))
([6c4c8ab](https://github.com/LucasSantana-Dev/Lucky/commit/6c4c8ab9a7488149dece8f486160ca3125d17a4e))
* **security:** bump vite 8.0.16 + form-data 4.0.6 for high advisories
([#1457](https://github.com/LucasSantana-Dev/Lucky/issues/1457))
([58d21d5](https://github.com/LucasSantana-Dev/Lucky/commit/58d21d56ad437bb5526dd5dcc9e5af3603d4b310))
* **security:** pass staging webhook secret via env not argv
([#1600](https://github.com/LucasSantana-Dev/Lucky/issues/1600))
([d12efc5](https://github.com/LucasSantana-Dev/Lucky/commit/d12efc519570026cf9a6f1b075d57b99d41898e2))
* **security:** redact operational diagnostics from
/api/health/auth-config
([#1710](https://github.com/LucasSantana-Dev/Lucky/issues/1710))
([e1b6b61](https://github.com/LucasSantana-Dev/Lucky/commit/e1b6b61c493eabd4d633d9600c46ad29a3ffc781))
* **security:** redact secrets/PII from logs
([#1208](https://github.com/LucasSantana-Dev/Lucky/issues/1208))
([#1220](https://github.com/LucasSantana-Dev/Lucky/issues/1220))
([2a09f90](https://github.com/LucasSantana-Dev/Lucky/commit/2a09f900c87e9ca1ef8c50a5ece6b1d7eecbc11e))
* **security:** resolve CodeQL/Semgrep findings (XSS, cookie, log
injection, nginx headers)
([#1707](https://github.com/LucasSantana-Dev/Lucky/issues/1707))
([3a30135](https://github.com/LucasSantana-Dev/Lucky/commit/3a301358d7cae1091bcbb79a1ff17c638317e641))
* **security:** verify bot authorship before trusting slug marker
([#1599](https://github.com/LucasSantana-Dev/Lucky/issues/1599))
([23bf73a](https://github.com/LucasSantana-Dev/Lucky/commit/23bf73a3e7db054d63ab7faea60db66124fbbfe9))
* **shared:** drop buggy token-overlap util + optimize levenshtein
([#1246](https://github.com/LucasSantana-Dev/Lucky/issues/1246))
([5b65d47](https://github.com/LucasSantana-Dev/Lucky/commit/5b65d4768f0e3bf19ca9e211957ce2fec07ef4f6))
* **shared:** env-isolate environment.test.ts (no secret dumps)
([#1292](https://github.com/LucasSantana-Dev/Lucky/issues/1292))
([588037c](https://github.com/LucasSantana-Dev/Lucky/commit/588037cf8b3a7424ad922b15d28aeb8548eb082e))
* **shared:** export ./utils/monitoring subpath — fixes lucky-bot
crash-loop
([#1105](https://github.com/LucasSantana-Dev/Lucky/issues/1105))
([2c959f3](https://github.com/LucasSantana-Dev/Lucky/commit/2c959f3d9765acdedab969faaaa229869a657ded))
* **shared:** export config/* subpath for prod esm resolution
([#1250](https://github.com/LucasSantana-Dev/Lucky/issues/1250))
([f4167a8](https://github.com/LucasSantana-Dev/Lucky/commit/f4167a80f2b78a693e9aacf5744358137e400f17))
* **shared:** export utils/support subpath for prod esm resolution
([#1248](https://github.com/LucasSantana-Dev/Lucky/issues/1248))
([8d3c092](https://github.com/LucasSantana-Dev/Lucky/commit/8d3c09265997e360e050d9006b55e12c8320abf3))
* **shared:** guard JSON.parse on embed data in CustomCommandService
([#1168](https://github.com/LucasSantana-Dev/Lucky/issues/1168))
([1c46b55](https://github.com/LucasSantana-Dev/Lucky/commit/1c46b557d7bcd5c847aca14c0562cae8a9bb77a0))
* **shared:** log db error in feature-toggle override read
([#1286](https://github.com/LucasSantana-Dev/Lucky/issues/1286))
([#1411](https://github.com/LucasSantana-Dev/Lucky/issues/1411))
([0dfc409](https://github.com/LucasSantana-Dev/Lucky/commit/0dfc4091c1e688569f656851b39f06716eecb4a0))
* **shared:** make LevelService.addXP atomic to prevent lost XP under
concurrency
([#1178](https://github.com/LucasSantana-Dev/Lucky/issues/1178))
([d1edffe](https://github.com/LucasSantana-Dev/Lucky/commit/d1edffe1c410b7393e184c796edeec83e4a17cae))
* **shared:** make read-then-write service paths atomic
([#1199](https://github.com/LucasSantana-Dev/Lucky/issues/1199))
([#1340](https://github.com/LucasSantana-Dev/Lucky/issues/1340))
([ba1b840](https://github.com/LucasSantana-Dev/Lucky/commit/ba1b840accb4858837c0b46e8018b4d1bcd53291))
* **shared:** normalize embed template name on gettemplate
([#1327](https://github.com/LucasSantana-Dev/Lucky/issues/1327))
([#1350](https://github.com/LucasSantana-Dev/Lucky/issues/1350))
([d221b57](https://github.com/LucasSantana-Dev/Lucky/commit/d221b577db03473f0930747362c65861aae501fa))
* **shared:** safe env parsing via parseIntEnv helper
([#1209](https://github.com/LucasSantana-Dev/Lucky/issues/1209))
([#1335](https://github.com/LucasSantana-Dev/Lucky/issues/1335))
([32e3684](https://github.com/LucasSantana-Dev/Lucky/commit/32e36849ac0b8c9b3e839fc24a97f1f6c1972376))
* **shared:** surface Redis client init errors instead of silent swallow
([#1176](https://github.com/LucasSantana-Dev/Lucky/issues/1176))
([157c14e](https://github.com/LucasSantana-Dev/Lucky/commit/157c14ec558a5fffd54e34400eb6a0b02a5a2763))
* **shared:** validate EmbedData shape with Zod before storing custom
commands
([#1179](https://github.com/LucasSantana-Dev/Lucky/issues/1179))
([7419b0e](https://github.com/LucasSantana-Dev/Lucky/commit/7419b0e1f4a4547b8a019c184fe63a41c2dcb017))
* **shared:** validate guildautomation json on read
([#1194](https://github.com/LucasSantana-Dev/Lucky/issues/1194))
([#1346](https://github.com/LucasSantana-Dev/Lucky/issues/1346))
([93d9eea](https://github.com/LucasSantana-Dev/Lucky/commit/93d9eea104c989485b125eb2de494a8032c2f6b4))
* **shared:** wrap ModerationService.createCase in transaction to
prevent duplicate case numbers
([#1167](https://github.com/LucasSantana-Dev/Lucky/issues/1167))
([be52580](https://github.com/LucasSantana-Dev/Lucky/commit/be5258049b6826c4a7141d4b00a8b6f6777d332e))
* **sonar:** clear main reliability gate - s1244 and tailwind v4 fps
([#1671](https://github.com/LucasSantana-Dev/Lucky/issues/1671))
([c12059d](https://github.com/LucasSantana-Dev/Lucky/commit/c12059dfc059db1915706723659812b088c5f34c))
* **spotify:** log oauth token-exchange failures
([#1286](https://github.com/LucasSantana-Dev/Lucky/issues/1286) track b)
([8306c35](https://github.com/LucasSantana-Dev/Lucky/commit/8306c35b19587d5b59e8092c0e245a2ed087b658))
* **telemetry:** un-silence skip-reason emoji prefill errors
([#1660](https://github.com/LucasSantana-Dev/Lucky/issues/1660))
([5eabbd2](https://github.com/LucasSantana-Dev/Lucky/commit/5eabbd2bad04ee92885766ba7184219ea17e5758))
* **test:** close open handles causing jest force-exit in bot suite
([#1605](https://github.com/LucasSantana-Dev/Lucky/issues/1605))
([cf2a026](https://github.com/LucasSantana-Dev/Lucky/commit/cf2a026d4852e2889eb18e1a0ce2389d73da3333))
* **twitch:** add debug logging for skipped channel notifications
([#947](https://github.com/LucasSantana-Dev/Lucky/issues/947))
([0dcf1c2](https://github.com/LucasSantana-Dev/Lucky/commit/0dcf1c2e5c32cda64f80e21f20a9e888715f6ca8))
* **twitch:** re-subscribe to EventSub after unexpected reconnect
([#870](https://github.com/LucasSantana-Dev/Lucky/issues/870))
([#1395](https://github.com/LucasSantana-Dev/Lucky/issues/1395))
([78a30f3](https://github.com/LucasSantana-Dev/Lucky/commit/78a30f31b9e97bd9e5fe86397ce5bdca272c0c10))
* **twitch:** refresh bot subscriptions on web add/remove
([#870](https://github.com/LucasSantana-Dev/Lucky/issues/870))
([939d4b3](https://github.com/LucasSantana-Dev/Lucky/commit/939d4b3721158d0c52c2f9c7944709baf38d35c0))
* **ui:** address CodeRabbit findings on
[#856](https://github.com/LucasSantana-Dev/Lucky/issues/856)
([56f2c82](https://github.com/LucasSantana-Dev/Lucky/commit/56f2c823eeec6bf2d468595fec509284b31e82da))
* **web:** clear auth check promise on settle, not via 100ms timer
([#1311](https://github.com/LucasSantana-Dev/Lucky/issues/1311))
([5cc8eef](https://github.com/LucasSantana-Dev/Lucky/commit/5cc8eefd7d83a5319175b056616ffe097a031299))
* **web:** GuildAutomation error state when both fetches reject
([#1144](https://github.com/LucasSantana-Dev/Lucky/issues/1144))
([f789aa3](https://github.com/LucasSantana-Dev/Lucky/commit/f789aa3e0e110958a0d56230c8273caaca4e6a85))
* **web:** language dropdown switches app language via radio group
([d4fdd98](https://github.com/LucasSantana-Dev/Lucky/commit/d4fdd9880f1246eb985b1214899302eb7b115192))
* **web:** relabel landing RepoCard stats to real servers/users
([#1145](https://github.com/LucasSantana-Dev/Lucky/issues/1145))
([2d63983](https://github.com/LucasSantana-Dev/Lucky/commit/2d6398374f9f0081575992d978c7f57f22005858))
* **web:** remove dead featuresStore toggle code + rollback on failure
([#1147](https://github.com/LucasSantana-Dev/Lucky/issues/1147))
([f8697fb](https://github.com/LucasSantana-Dev/Lucky/commit/f8697fb36532a76f5106dd0fb1bc9d13e5351c71))
* **web:** report swallowed member-context fetch error to Sentry
([#1286](https://github.com/LucasSantana-Dev/Lucky/issues/1286) B3)
([#1416](https://github.com/LucasSantana-Dev/Lucky/issues/1416))
([85f141d](https://github.com/LucasSantana-Dev/Lucky/commit/85f141d7926ef9eeec4a1195dda9702df25d7c02))
* **web:** route handled errors to Sentry, enforce no-console
([#1296](https://github.com/LucasSantana-Dev/Lucky/issues/1296))
([a34e777](https://github.com/LucasSantana-Dev/Lucky/commit/a34e777d1627ad3a2715b49f211c4b7bd3e74266))
* **web:** surface swallowed fetch errors instead of silent catch
([#1254](https://github.com/LucasSantana-Dev/Lucky/issues/1254))
([6afb0cd](https://github.com/LucasSantana-Dev/Lucky/commit/6afb0cd4f1a81f5c5e1616c7925c7bc75b9524b6))
### Performance Improvements
* **bot:** bound autoplay Maps + parallelize replenisher awaits
([#1215](https://github.com/LucasSantana-Dev/Lucky/issues/1215))
([1e55afa](https://github.com/LucasSantana-Dev/Lucky/commit/1e55afa125acbb60f0b1d28ff9c168a5e32b8ead))
* **bot:** bound external scrobbler track cache with lru+ttl
([#1282](https://github.com/LucasSantana-Dev/Lucky/issues/1282))
([#1316](https://github.com/LucasSantana-Dev/Lucky/issues/1316))
([7f29efc](https://github.com/LucasSantana-Dev/Lucky/commit/7f29efce0ea9ad0b6ad1dff6edfae57d4f15b2f8))
* bound unbounded findMany queries
([#1206](https://github.com/LucasSantana-Dev/Lucky/issues/1206))
([#1214](https://github.com/LucasSantana-Dev/Lucky/issues/1214))
([cdc0082](https://github.com/LucasSantana-Dev/Lucky/commit/cdc0082b64f407c313850e00864055b669bec3d8))
* **shared:** batch recommendation telemetry counts in one groupBy
([#1308](https://github.com/LucasSantana-Dev/Lucky/issues/1308))
([e5a5973](https://github.com/LucasSantana-Dev/Lucky/commit/e5a5973d9c25d5926ab576b13265fe05c2d87032))
</details>
---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Release 2.33.0 ships smarter autoplay, new role management in the
dashboard, better moderation and Twitch integrations, and stronger
observability/security. It also includes wide-ranging fixes and
performance improvements across bot, backend, and web.
- **New Features**
- Autoplay scoring upgrades: implicit dislike penalty, recency decay,
replay boost, and evaluation harness.
- Dashboard: role groups and reaction roles management with editor
(emoji picker, media, import/export).
- Moderation and guild tools: move message via context menu, batch
operations (bulk move), AFK, reminders, giveaways, smart custom
commands, starboard seeding.
- Integrations: Twitch follower/subscriber role sync and new EventSub
events; RSS bridge and weekly digest.
- Backend/Web: support intake with admin views, Postgres session store,
server logs/settings pages, previous-track command, per-route SEO and
sitemap.
- Observability/Security: request-id correlation, deploy markers/alerts,
CSP headers and violation collection.
- **Bug Fixes**
- Timeouts and guardrails on external calls with graceful degradation;
mitigations for Discord 429 storms.
- Hardening for reaction roles, role writes, guild route validation,
JSON parsing, and DB constraints; atomic write paths.
- Bot stability: safer session restore and shutdown, extractor
registration, clearer YouTube errors, accurate previous button replies.
- CI/CD and deploy reliability: SHA-pinned deploys, health probes, cache
correctness, pinned actions, verified frontend caching.
- Security: dependency updates, redacted logs/health output, and
CodeQL/Semgrep findings resolved.
<sup>Written for commit 22727a164df9bd407b3493dd3459ca46984664c4.
Summary will update on new commits.</sup>
<a
href="https://cubic.dev/pr/LucasSantana-Dev/Lucky/pull/1733?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Documentation**
* Added release notes for version 2.33.0, highlighting new features, bug
fixes, and performance improvements.
* **Chores**
* Updated the project version to 2.33.0.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->



Overhauls the Reaction Roles dashboard form per the requested scope. Built in phases, each slice test-first and spec+quality reviewed (all APPROVED); migration verified by prisma-migration-verifier.
Features
PUT /api/guilds/:id/reaction-roles/:messageId: PATCHes the Discord message (embed + buttons) and reconciles role mappings in a transaction; channel is immutable on edit. SharedMessageForm(create/edit), pencil button per message.title/description/imageUrlonReactionRoleMessage(+ migration) so the edit form prefills the current content.**, italic*, underline__, strikethrough~~, spoiler||(wraps the selection).GET /api/guilds/:id/emojis; inserts unicode or<:name:id>.attachment://, multipart, no persistent storage; size/mime capped). File takes precedence over URL.Backend
updateReactionRoleMessage,getGuildEmojis;imageUrl/imageFileon create/update;buildButtonRows/assertSnowflakesshared by create+update (DRY). Multipart reuses the existingmultermiddleware; the JSON path is unchanged.20260623065023_add_reaction_role_embed_content(3 nullable columns, non-destructive).Tests
Not visually verified
The page is behind auth, so the build agents couldn't capture screenshots — UI is code/test/slop-audit-verified only. Recommend a smoke test:
cd packages/frontend && npm run dev→ select a guild → Reaction Roles (verify create/edit dialogs, emoji picker, formatting, file upload, export/import).Note: #1540 (parseEmoji return type, Discord response validation, service-level roleId validation) is NOT closed by this PR — it remains a separate hardening follow-up.
Summary by cubic
Overhauls the Reaction Roles dashboard with an editable message form, emoji picker, formatting tools, media (URL + upload), and JSON export/import. Adds security hardening (encoded Discord REST URLs, inline snowflake ID guards, http/https-only previews, 502 error mapping, a shared Discord request builder, bounded multipart limits) and a typed multipart payload parser/upload middleware to eliminate any/unsafe code.
New Features
PUT /api/guilds/:guildId/reaction-roles/:messageId; channel locked; up to 25 buttons; duplicateroleIds blocked.ReactionRoleMessage(title,description,imageUrl) for edit prefill.GET /api/guilds/:guildId/emojis(lazy fetch).Migration
title,description, andimageUrlcolumns toReactionRoleMessage.Written for commit 7a26932. Summary will update on new commits.
Summary by CodeRabbit
Release Notes
New Features
Improvements
API/Updates