Skip to content

feat(reaction-roles): editable form, emoji picker, formatting, media, export/import - #1544

Merged
LucasSantana-Dev merged 15 commits into
mainfrom
feat/reaction-roles-form-overhaul
Jun 23, 2026
Merged

LucasSantana-Dev merged 15 commits into
mainfrom
feat/reaction-roles-form-overhaul

Conversation

@LucasSantana-Dev

@LucasSantana-Dev LucasSantana-Dev commented Jun 23, 2026 •

Copy link
Copy Markdown
Owner

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

  • Edit existing messages — 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. Shared MessageForm (create/edit), pencil button per message.
  • Persisted embed content — new nullable title/description/imageUrl on ReactionRoleMessage (+ migration) so the edit form prefills the current content.
  • Expandable description — auto-grow textarea (3→12 rows).
  • Formatting toolbar — Discord markdown: bold **, italic *, underline __, strikethrough ~~, spoiler || (wraps the selection).
  • Emoji picker (custom, no new dep) — unicode grid + a Server tab fetching the guild's custom emojis via new GET /api/guilds/:id/emojis; inserts unicode or <:name:id>.
  • Media — optional image URL (embed image, live preview) and image file upload → forwarded to Discord as a message attachment (attachment://, multipart, no persistent storage; size/mime capped). File takes precedence over URL.
  • Sticky "Add role" — the roles-section header sticks to the top of the dialog scroll area.
  • JSON export + import — export the guild's messages to a re-importable JSON file (download + copy, ids omitted); import validates before any network call and bulk-creates, collecting per-item errors without aborting the batch.

Backend

  • New: updateReactionRoleMessage, getGuildEmojis; imageUrl/imageFile on create/update; buildButtonRows/assertSnowflakes shared by create+update (DRY). Multipart reuses the existing multer middleware; the JSON path is unchanged.
  • Migration: 20260623065023_add_reaction_role_embed_content (3 nullable columns, non-destructive).

Tests

  • ReactionRolesService 79 · GuildService 53 · roles integration 21 · frontend reaction-roles 54 (+ export util 19) · all RED→GREEN, full frontend suite 803 green; backend + frontend + shared tsc/eslint clean.

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

    • Edit existing messages via PUT /api/guilds/:guildId/reaction-roles/:messageId; channel locked; up to 25 buttons; duplicate roleIds blocked.
    • Persist embed fields on ReactionRoleMessage (title, description, imageUrl) for edit prefill.
    • Emoji picker with unicode + server emojis via GET /api/guilds/:guildId/emojis (lazy fetch).
    • Formatting toolbar for Discord markdown; auto-growing description textarea.
    • Media: image URL or file upload (multipart) sent as an attachment; file wins; 8MB + MIME checks; bounded multipart limits (files/fields/parts/fieldSize); no storage.
    • JSON export and import with client-side validation and per-item errors; bulk create without aborting the batch.
  • Migration

    • Run the Prisma migration to add nullable title, description, and imageUrl columns to ReactionRoleMessage.

Written for commit 7a26932. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

Release Notes

  • New Features

    • Reaction roles now support titles, descriptions, and images (upload or image URL), with full edit support for existing messages.
    • Added reaction-role export/import via JSON, including an import dialog.
    • Introduced a custom emoji picker that loads server emojis.
  • Improvements

    • Image uploads are validated for type and size, with clearer error responses.
  • API/Updates

    • Added an endpoint to fetch guild emojis for the picker.
    • Reaction-role create/update now persists embed content (title/description/imageUrl).

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).
@vercel

vercel Bot commented Jun 23, 2026 •

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
lucky Ready Ready Preview, Comment Jun 23, 2026 1:54pm

@github-actions

Copy link
Copy Markdown

Failed to generate code suggestions for PR

@github-actions

github-actions Bot commented Jun 23, 2026 •

Copy link
Copy Markdown
Warnings
⚠️

Big PR — 5653 lines changed across 27 files. Consider splitting into smaller, reviewable chunks.

⚠️

User-facing change without a CHANGELOG.md update. Add a line under ## [Unreleased] if this should appear in release notes. (Or apply the skip-changelog label if this PR does not affect end users.)

Generated by 🚫 dangerJS against 7a26932

@coderabbitai

coderabbitai Bot commented Jun 23, 2026 •

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

Adds full edit support for reaction-role dashboard messages, image upload via multipart form data, embed fields (title, description, imageUrl) persisted in a new Prisma migration, a GuildService.getGuildEmojis endpoint with bot-client/REST fallback, export/import JSON utilities, and a reworked React page with EmojiPicker, AutoGrowTextarea, FormattingToolbar, and ImportDialog components.

Changes

Reaction Roles Dashboard Enhancement

Layer / File(s) Summary
DB schema, Prisma migration, and shared data contracts
prisma/migrations/20260623065023_add_reaction_role_embed_content/migration.sql, prisma/schema.prisma, packages/shared/src/services/ReactionRolesService/index.ts, packages/frontend/src/types/guild.ts, packages/frontend/src/services/reactionRolesApi.ts, packages/backend/src/schemas/management.ts, packages/backend/src/services/GuildService.ts
Adds nullable title, description, and imageUrl columns to reaction_role_messages via migration and Prisma schema. Defines DashboardUpdateReactionRoleOptions, GuildEmojiOption, and UpdateReactionRolePayload interfaces. Extends ReactionRoleMessage and CreateReactionRolePayload types. Adds imageUrl field validation to management Zod schemas.
GuildService.getGuildEmojis and API endpoint
packages/backend/src/services/GuildService.ts, packages/backend/src/routes/guilds.ts, packages/frontend/src/services/api.ts, packages/backend/tests/unit/services/GuildService.emojis.test.ts
Implements getGuildEmojis with bot-client emoji cache path and Discord REST fallback (10s timeout, AbortSignal). Adds GET /api/guilds/:guildId/emojis route with requireGuildModuleAccess('overview') check. Wires api.guilds.getEmojis on frontend. Unit tests cover bot-client resolution, REST fallback, filtering, error propagation, network failures, and empty-cache cases.
ReactionRolesService: updateReactionRoleMessage and createFromDashboard refactor
packages/shared/src/services/ReactionRolesService/index.ts, packages/shared/src/services/ReactionRolesService/index.spec.ts
Extracts assertSnowflakes and buildButtonRows helpers for ID validation and button payload construction. Refactors createReactionRoleMessageFromDashboard to use them and support multipart/JSON image posting. Adds updateReactionRoleMessage implementing PATCH flow: validates ownership, rebuilds button rows, PATCHes Discord with optional FormData, and updates mappings via Prisma $transaction. Spec coverage updated to use valid snowflake role IDs; extensive tests for edit, image, and multipart upload flows.
Backend routes: multer middleware, POST/PUT handlers, error mapping
packages/backend/src/routes/roles.ts, packages/backend/src/errors/AppError.ts, packages/backend/tests/integration/routes/roles.test.ts
Adds multer in-memory upload middleware (8 MB, PNG/JPEG/GIF/WebP, bounds multipart parts/fields) with imageUploadHandler wrapper converting upload errors to AppError (413 for size, 400 for type). Refactors POST and PUT reaction-roles routes to parse multipart/JSON, validate via safeParse, construct optional imageFile, require DISCORD_TOKEN, and call service methods with mode-specific error mapping. Adds AppError.badGateway factory. Extends role management endpoints to map Discord API/bot token errors to bad-gateway. Integration tests cover multipart upload success/failure, JSON-only fallback, and update-with-file scenarios.
Frontend API: reactionRolesApi FormData support
packages/frontend/src/services/reactionRolesApi.ts, packages/frontend/src/services/reactionRolesApi.test.ts
create and update accept optional imageFile?: File and switch to multipart FormData when provided. Tests verify FormData structure (image, JSON-stringified payload) for both methods.
Export/import utilities
packages/frontend/src/utils/reactionRolesExport.ts, packages/frontend/src/utils/reactionRolesExport.test.ts
serializeReactionRolesToJSON maps ReactionRoleMessage[] to ExportedReactionRole[], omitting identifying metadata. deserializeReactionRolesJSON parses and validates JSON with snowflake format, length, role count, and style constraints, returning a DeserializeResult with accumulated errors. Tests cover serialization omissions and comprehensive deserialization error/success cases.
New UI components: AutoGrowTextarea, FormattingToolbar, EmojiPicker, ImportDialog
packages/frontend/src/components/ui/AutoGrowTextarea.tsx, packages/frontend/src/components/ui/FormattingToolbar.tsx, packages/frontend/src/components/ui/EmojiPicker.tsx, packages/frontend/src/components/reactionRoles/ImportDialog.tsx, packages/frontend/src/components/{ui,reactionRoles}/*.test.tsx
AutoGrowTextarea auto-adjusts height between minRows/maxRows via scrollHeight calculation. FormattingToolbar wraps or inserts markdown-like markers (bold, italic, underline, strikethrough, spoiler) via a textarea ref. EmojiPicker displays tabbed standard and server emoji dropdown, lazily fetching server emojis from guild emoji API with image error fallback. ImportDialog handles JSON paste/file import with per-item sequential API calls, progress updates, and per-index error collection. Comprehensive component tests verify rendering, interaction, state management, and error paths.
ReactionRoles page overhaul: unified MessageForm with edit/create/image/export/import
packages/frontend/src/pages/ReactionRoles.tsx, packages/frontend/src/pages/ReactionRoles.test.tsx
Replaces create-only dialog with unified MessageForm supporting create/edit modes. In edit mode, pre-fills all fields from existing message and disables channel selection. Adds image URL input with preview and file upload with preview. Reworks role mappings into card-based editors with EmojiPicker, FormattingToolbar, AutoGrowTextarea for description, and enforced max 25 roles. Form submission branches on mode: create calls api.reactionRoles.create, edit calls api.reactionRoles.update. Implements Export (JSON download) and Import (opens ImportDialog) header actions. MessageCard gains onEdit callback and Edit button. Page manages unified form/import state. Comprehensive tests cover edit/create dialog flows, image URL/file upload, auto-grow textarea, export state and download, import validation/success/partial-failure, form validation, role constraints, and payload branching for image URL vs file paths.
SonarQube configuration
sonar-project.properties
Ignores typescript:S5693 (request content-length limit) for reaction-roles image upload routes in packages/backend/src/routes/roles.ts.

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
Loading
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
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related issues

  • #1540 — The PR adds assertSnowflakes validation for roleId in the updated buildButtonRows and new updateReactionRoleMessage methods, alongside Discord API response validation in createReactionRoleMessageFromDashboard, directly addressing the service hardening and ID validation concerns in that issue.

Possibly related PRs

  • LucasSantana-Dev/Lucky#1532: This PR directly extends the reaction-roles dashboard REST POST/PUT endpoints and ReactionRolesService introduced in that PR by adding image upload, edit support (updateReactionRoleMessage), and embed field persistence.
  • LucasSantana-Dev/Lucky#392: Both PRs modify packages/frontend/src/pages/ReactionRoles.test.tsx with substantial test additions for Reaction Roles UI rendering and form/dialog interaction flows at the same file level.
  • LucasSantana-Dev/Lucky#165: The new GET /api/guilds/:guildId/emojis route uses requireGuildModuleAccess('overview'), which is part of the RBAC access control framework introduced in that PR.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 2.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the primary features added: editable reaction-role forms, emoji picker, markdown formatting, media support, and import/export functionality.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/reaction-roles-form-overhaul

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread packages/backend/src/services/GuildService.ts Fixed
Comment thread packages/frontend/src/pages/ReactionRoles.tsx Fixed
Comment thread packages/frontend/src/pages/ReactionRoles.tsx
Comment thread packages/shared/src/services/ReactionRolesService/index.ts Fixed
Comment thread packages/backend/tests/integration/routes/roles.test.ts Fixed
@github-actions

github-actions Bot commented Jun 23, 2026 •

Copy link
Copy Markdown

Size Change: +5.04 kB (+1.12%)

Total Size: 453 kB

📦 View Changed
Filename Size Change
packages/frontend/dist/assets/Admin-DL3yBZXe.js 0 B -2.3 kB (removed) 🏆
packages/frontend/dist/assets/Admin-rMz1u-AZ.js 2.3 kB +2.3 kB (new file) 🆕
packages/frontend/dist/assets/AdminSupport-BLtW0vTW.js 0 B -1.57 kB (removed) 🏆
packages/frontend/dist/assets/AdminSupport-Ca5TZsiK.js 1.57 kB +1.57 kB (new file) 🆕
packages/frontend/dist/assets/api-BzWozooH.js 3.78 kB +3.78 kB (new file) 🆕
packages/frontend/dist/assets/api-CcFW_8dE.js 0 B -3.67 kB (removed) 🏆
packages/frontend/dist/assets/AutoMessages-CY4RERKG.js 2.67 kB +2.67 kB (new file) 🆕
packages/frontend/dist/assets/AutoMessages-D_Sg8a36.js 0 B -2.67 kB (removed) 🏆
packages/frontend/dist/assets/AutoMod-9hqIavEM.js 0 B -4.16 kB (removed) 🏆
packages/frontend/dist/assets/AutoMod-C6qttZaf.js 4.17 kB +4.17 kB (new file) 🆕
packages/frontend/dist/assets/badge-6OJmhUY9.js 0 B -501 B (removed) 🏆
packages/frontend/dist/assets/badge-Cyf9WI3E.js 501 B +501 B (new file) 🆕
packages/frontend/dist/assets/Card-CWzXGqFf.js 505 B +505 B (new file) 🆕
packages/frontend/dist/assets/Card-Dqyh8neO.js 0 B -506 B (removed) 🏆
packages/frontend/dist/assets/Changelog-BanXTAbh.js 38.3 kB +38.3 kB (new file) 🆕
packages/frontend/dist/assets/Changelog-CWh5TJxQ.js 0 B -38.3 kB (removed) 🏆
packages/frontend/dist/assets/CommandsConfig-CHaNi-nc.js 0 B -1.5 kB (removed) 🏆
packages/frontend/dist/assets/CommandsConfig-Droq50nK.js 1.5 kB +1.5 kB (new file) 🆕
packages/frontend/dist/assets/Config-B-1TJ66n.js 0 B -1.95 kB (removed) 🏆
packages/frontend/dist/assets/Config-D7i0-1wO.js 1.95 kB +1.95 kB (new file) 🆕
packages/frontend/dist/assets/CustomCommands-DCTdhuaB.js 2.11 kB +2.11 kB (new file) 🆕
packages/frontend/dist/assets/CustomCommands-DNi_Z-nj.js 0 B -2.11 kB (removed) 🏆
packages/frontend/dist/assets/DashboardOverview-C37tRW0d.js 3.9 kB +3.9 kB (new file) 🆕
packages/frontend/dist/assets/DashboardOverview-CKUN2YKd.js 0 B -3.9 kB (removed) 🏆
packages/frontend/dist/assets/dialog-Cjv6BVkQ.js 0 B -956 B (removed) 🏆
packages/frontend/dist/assets/dialog-Wr0drB1Q.js 957 B +957 B (new file) 🆕
packages/frontend/dist/assets/Docs-C6gntWLC.js 17.6 kB +17.6 kB (new file) 🆕
packages/frontend/dist/assets/Docs-DICKcoJo.js 0 B -17.6 kB (removed) 🏆
packages/frontend/dist/assets/DocsShell-70hUM-Ab.js 0 B -1.42 kB (removed) 🏆
packages/frontend/dist/assets/DocsShell-C3fBkagq.js 1.42 kB +1.42 kB (new file) 🆕
packages/frontend/dist/assets/EmbedBuilder-Ci8pRT0b.js 0 B -3.32 kB (removed) 🏆
packages/frontend/dist/assets/EmbedBuilder-CpEEhQCu.js 3.32 kB +3.32 kB (new file) 🆕
packages/frontend/dist/assets/Features-1qtav5a7.js 755 B +755 B (new file) 🆕
packages/frontend/dist/assets/Features-CofovuZ3.js 0 B -754 B (removed) 🏆
packages/frontend/dist/assets/GuildAutomation-CsYZfDVE.js 0 B -3 kB (removed) 🏆
packages/frontend/dist/assets/GuildAutomation-DJfujXTK.js 3 kB +3 kB (new file) 🆕
packages/frontend/dist/assets/index-B4t21kKt.js 0 B -56.7 kB (removed) 🏆
packages/frontend/dist/assets/index-CF5vfQpU.js 56.7 kB +56.7 kB (new file) 🆕
packages/frontend/dist/assets/index-DFAX3KXt.css 0 B -18.1 kB (removed) 🏆
packages/frontend/dist/assets/index-TVLDmMPK.css 18.2 kB +18.2 kB (new file) 🆕
packages/frontend/dist/assets/input-B9YLXi9p.js 0 B -465 B (removed) 🏆
packages/frontend/dist/assets/input-CjufYqD3.js 463 B +463 B (new file) 🆕
packages/frontend/dist/assets/label-1R1-aiZY.js 476 B +476 B (new file) 🆕
packages/frontend/dist/assets/label-VdQdlQYZ.js 0 B -476 B (removed) 🏆
packages/frontend/dist/assets/Landing-CZ5Ev9km.js 0 B -5.2 kB (removed) 🏆
packages/frontend/dist/assets/Landing-Da821DYa.js 5.2 kB +5.2 kB (new file) 🆕
packages/frontend/dist/assets/LastFm-AdIg_23J.js 1.94 kB +1.94 kB (new file) 🆕
packages/frontend/dist/assets/LastFm-DloCoAtb.js 0 B -1.93 kB (removed) 🏆
packages/frontend/dist/assets/Levels-BLMn-zMv.js 0 B -2.27 kB (removed) 🏆
packages/frontend/dist/assets/Levels-yfGfm1UA.js 2.27 kB +2.27 kB (new file) 🆕
packages/frontend/dist/assets/Login-CRYZZqeC.js 0 B -2.49 kB (removed) 🏆
packages/frontend/dist/assets/Login-DmG9XD_m.js 2.5 kB +2.5 kB (new file) 🆕
packages/frontend/dist/assets/Lyrics-BN5YNVKM.js 1.34 kB +1.34 kB (new file) 🆕
packages/frontend/dist/assets/Lyrics-DQjxtp4N.js 0 B -1.33 kB (removed) 🏆
packages/frontend/dist/assets/Moderation-DngAPiHd.js 3.77 kB +3.77 kB (new file) 🆕
packages/frontend/dist/assets/Moderation-ItvuTZuz.js 0 B -3.76 kB (removed) 🏆
packages/frontend/dist/assets/Music-BtZb8use.js 0 B -5.88 kB (removed) 🏆
packages/frontend/dist/assets/Music-DKXwGLp6.js 5.91 kB +5.91 kB (new file) 🆕
packages/frontend/dist/assets/MusicConfig-BttHyXlT.js 1.65 kB +1.65 kB (new file) 🆕
packages/frontend/dist/assets/MusicConfig-CpZdaQaB.js 0 B -1.64 kB (removed) 🏆
packages/frontend/dist/assets/PreferredArtists-2lrkGeXG.js 0 B -3.79 kB (removed) 🏆
packages/frontend/dist/assets/PreferredArtists-Dy46LZuV.js 3.79 kB +3.79 kB (new file) 🆕
packages/frontend/dist/assets/PrivacyPolicy-CwF4_xWp.js 0 B -1.77 kB (removed) 🏆
packages/frontend/dist/assets/PrivacyPolicy-DiVfA0Nw.js 1.77 kB +1.77 kB (new file) 🆕
packages/frontend/dist/assets/ReactionRoles-B6QB69xr.js 0 B -3.48 kB (removed) 🏆
packages/frontend/dist/assets/ReactionRoles-DGDs3anF.js 7.97 kB +7.97 kB (new file) 🆕
packages/frontend/dist/assets/Roles-CoZTTooF.js 3.33 kB +3.33 kB (new file) 🆕
packages/frontend/dist/assets/Roles-DkY-1yD9.js 0 B -3.33 kB (removed) 🏆
packages/frontend/dist/assets/SectionHeader-D3cCS5fg.js 894 B +894 B (new file) 🆕
packages/frontend/dist/assets/SectionHeader-Dc60SOwD.js 0 B -894 B (removed) 🏆
packages/frontend/dist/assets/select-DsnfQ2MZ.js 1.23 kB +1.23 kB (new file) 🆕
packages/frontend/dist/assets/select-VykL0HZK.js 0 B -1.23 kB (removed) 🏆
packages/frontend/dist/assets/ServerLogs-BdRKwBJd.js 3.06 kB +3.06 kB (new file) 🆕
packages/frontend/dist/assets/ServerLogs-DmxF0y40.js 0 B -3.06 kB (removed) 🏆
packages/frontend/dist/assets/ServerSettings-BnnFv57l.js 0 B -4.17 kB (removed) 🏆
packages/frontend/dist/assets/ServerSettings-z-oqxFXx.js 4.17 kB +4.17 kB (new file) 🆕
packages/frontend/dist/assets/ServersPage-BwQKf07O.js 0 B -3 kB (removed) 🏆
packages/frontend/dist/assets/ServersPage-CufneTGQ.js 2.99 kB +2.99 kB (new file) 🆕
packages/frontend/dist/assets/Skeleton-CLrCJPjB.js 235 B +235 B (new file) 🆕
packages/frontend/dist/assets/Skeleton-JZzIJl46.js 0 B -238 B (removed) 🏆
packages/frontend/dist/assets/Spotify-BLmlL13j.js 1.94 kB +1.94 kB (new file) 🆕
packages/frontend/dist/assets/Spotify-C9WQu5IA.js 0 B -1.94 kB (removed) 🏆
packages/frontend/dist/assets/Starboard-Bxitlcmn.js 1.77 kB +1.77 kB (new file) 🆕
packages/frontend/dist/assets/Starboard-Cxjw296O.js 0 B -1.77 kB (removed) 🏆
packages/frontend/dist/assets/StatTile-BZoMzRgV.js 0 B -635 B (removed) 🏆
packages/frontend/dist/assets/StatTile-C_zapBcs.js 637 B +637 B (new file) 🆕
packages/frontend/dist/assets/Support-aVjuP4kS.js 0 B -1.53 kB (removed) 🏆
packages/frontend/dist/assets/Support-DuBJ9wBw.js 1.52 kB +1.52 kB (new file) 🆕
packages/frontend/dist/assets/switch-BQXdLpBR.js 542 B +542 B (new file) 🆕
packages/frontend/dist/assets/switch-Co09sIJ0.js 0 B -542 B (removed) 🏆
packages/frontend/dist/assets/TermsOfService-BNGXxF4N.js 1.59 kB +1.59 kB (new file) 🆕
packages/frontend/dist/assets/TermsOfService-DDrAYV0V.js 0 B -1.59 kB (removed) 🏆
packages/frontend/dist/assets/TrackHistory-DfmPsrRM.js 0 B -2.29 kB (removed) 🏆
packages/frontend/dist/assets/TrackHistory-HBi1YIUs.js 2.29 kB +2.29 kB (new file) 🆕
packages/frontend/dist/assets/TwitchNotifications-BmZQgCMa.js 2.43 kB +2.43 kB (new file) 🆕
packages/frontend/dist/assets/TwitchNotifications-O6tNJAHS.js 0 B -2.43 kB (removed) 🏆
packages/frontend/dist/assets/useActiveHeading-oFjIQqAd.js 1.36 kB +1.36 kB (new file) 🆕
packages/frontend/dist/assets/useActiveHeading-vie1NZWD.js 0 B -1.35 kB (removed) 🏆
packages/frontend/dist/assets/useFeatures-BiOGskko.js 0 B -2.06 kB (removed) 🏆
packages/frontend/dist/assets/useFeatures-R3CAOQbP.js 2.06 kB +2.06 kB (new file) 🆕
packages/frontend/dist/assets/vendor-ui-DLf1J8hB.js 65.7 kB +65.7 kB (new file) 🆕
packages/frontend/dist/assets/vendor-ui-suD8e4l8.js 0 B -65.4 kB (removed) 🏆
ℹ️ View Unchanged
Filename Size
packages/frontend/dist/assets/legalNav-B6k3CWsW.js 274 B
packages/frontend/dist/assets/rolldown-runtime-Cyuzqnbw.js 471 B
packages/frontend/dist/assets/routeMeta-BZjtwMbs.js 595 B
packages/frontend/dist/assets/sentry-DhXOA89y.js 3.76 kB
packages/frontend/dist/assets/usePageMetadata-DTv-6eVb.js 327 B
packages/frontend/dist/assets/vendor-forms-C-bof8GF.js 25.9 kB
packages/frontend/dist/assets/vendor-radix-DPjwWaDf.js 39.9 kB
packages/frontend/dist/assets/vendor-react-B2lWEVt_.js 55.7 kB
packages/frontend/dist/assets/vendor-state-CNyyWKJO.js 24.2 kB

compressed-size-action

@coderabbitai coderabbitai Bot added the enhancement New feature or request label Jun 23, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 win

Validate Discord create response messageId before DB write/cleanup paths.

Line 275 trusts resp.json().id without 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 win

Add 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

📥 Commits

Reviewing files that changed from the base of the PR and between 18a36ba and f327ddf.

📒 Files selected for processing (22)
  • packages/backend/src/routes/guilds.ts
  • packages/backend/src/routes/roles.ts
  • packages/backend/src/schemas/management.ts
  • packages/backend/src/services/GuildService.ts
  • packages/backend/tests/integration/routes/roles.test.ts
  • packages/backend/tests/unit/services/GuildService.emojis.test.ts
  • packages/frontend/src/components/reactionRoles/ImportDialog.tsx
  • packages/frontend/src/components/ui/AutoGrowTextarea.tsx
  • packages/frontend/src/components/ui/EmojiPicker.tsx
  • packages/frontend/src/components/ui/FormattingToolbar.tsx
  • packages/frontend/src/pages/ReactionRoles.test.tsx
  • packages/frontend/src/pages/ReactionRoles.tsx
  • packages/frontend/src/services/api.ts
  • packages/frontend/src/services/reactionRolesApi.test.ts
  • packages/frontend/src/services/reactionRolesApi.ts
  • packages/frontend/src/types/guild.ts
  • packages/frontend/src/utils/reactionRolesExport.test.ts
  • packages/frontend/src/utils/reactionRolesExport.ts
  • packages/shared/src/services/ReactionRolesService/index.spec.ts
  • packages/shared/src/services/ReactionRolesService/index.ts
  • prisma/migrations/20260623065023_add_reaction_role_embed_content/migration.sql
  • prisma/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 Quality

Tests at lines 823-1258 may lack proper scoped setup; verify structure and isolation.

This review comment claims tests are outside describe('ReactionRoles'), preventing the beforeEach from 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 beforeEach to ensure proper setup/teardown.

packages/shared/src/services/ReactionRolesService/index.ts (1)

166-174: 📐 Maintainability & Code Quality

Tighten parseEmoji return type to remove impossible null.

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 Correctness

Verify emoji cache strategy in getGuildEmojis implementation.

The review raises a valid concern about discord.js cache behavior—the guild.emojis.cache may be cold or incomplete if the GUILD_EMOJIS_AND_STICKERS gateway intent is not enabled, or if the cache was not pre-populated. However, the full code context is needed to confirm:

  1. Whether a fallback mechanism exists and how it's triggered
  2. Whether the early return at line 673 actually skips the intended fallback
  3. Whether explicit fetch() is necessary given the application's intent configuration

The 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.

Comment thread packages/backend/src/routes/roles.ts
Comment thread packages/frontend/src/pages/ReactionRoles.test.tsx
Comment thread packages/frontend/src/pages/ReactionRoles.tsx
Comment thread packages/frontend/src/pages/ReactionRoles.tsx
Comment thread packages/frontend/src/utils/reactionRolesExport.ts
Comment thread packages/shared/src/services/ReactionRolesService/index.ts

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread packages/shared/src/services/ReactionRolesService/index.ts
Comment thread packages/shared/src/services/ReactionRolesService/index.ts Outdated
Comment thread packages/shared/src/services/ReactionRolesService/index.ts
Comment thread packages/frontend/src/components/ui/AutoGrowTextarea.tsx
Comment thread packages/backend/src/services/GuildService.ts
Comment thread packages/frontend/src/pages/ReactionRoles.tsx Outdated
Comment thread packages/backend/src/routes/roles.ts Outdated
Comment thread packages/frontend/src/pages/ReactionRoles.tsx
Comment thread packages/frontend/src/pages/ReactionRoles.tsx
Comment thread packages/frontend/src/pages/ReactionRoles.test.tsx
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.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread packages/frontend/src/components/ui/EmojiPicker.test.tsx
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.
coderabbitai[bot]
coderabbitai Bot previously requested changes Jun 23, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.createObjectURL is 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 for revokeObjectURL) 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 win

Tests after Line 823 are outside the describe block and skip shared setup.

Everything after Line 823 runs without the beforeEach at Line 73, so mock state/default list setup is inconsistent and order-dependent. Keep these tests inside the same describe or add an equivalent top-level beforeEach.

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

📥 Commits

Reviewing files that changed from the base of the PR and between f327ddf and b33c609.

📒 Files selected for processing (4)
  • packages/frontend/src/components/reactionRoles/ImportDialog.test.tsx
  • packages/frontend/src/components/ui/EmojiPicker.test.tsx
  • packages/frontend/src/components/ui/FormattingToolbar.test.tsx
  • packages/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 assert api.reactionRoles.create call arguments.

Comment thread packages/frontend/src/components/ui/EmojiPicker.test.tsx
Comment thread packages/frontend/src/pages/ReactionRoles.test.tsx
Comment thread packages/frontend/src/pages/ReactionRoles.test.tsx
Comment thread packages/frontend/src/pages/ReactionRoles.test.tsx
Comment thread packages/frontend/src/pages/ReactionRoles.tsx

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread sonar-project.properties
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.
@LucasSantana-Dev

Copy link
Copy Markdown
Owner Author

Final review pass — remaining threads addressed or resolved with rationale:

  • SSRF (CodeQL/Sonar): cleared via inline snowflake validation at each Discord fetch sink (Sonar security gate now green).
  • S5693 content-length (the real Sonar vuln): multer now bounds files/fields/parts/fieldSize + 8MB; reviewed-safe, suppressed for the one route.
  • Lint: typed the multipart payload parser (no any/unused).
  • roleId validation in create: already enforced at the route boundary by reactionRoleEntrySchema (z.string().regex(/^\\d{17,20}$/)); createReactionRoleMessageFromDashboard is only reachable through that route, so a service-level re-check is redundant.
  • PATCH-before-DB on edit: inherent to updating an external system + a DB (can't be atomic); the create path compensates by deleting the orphan message, and edit failures surface as a 5xx. Acceptable known trade-off.
  • DOM-as-HTML (CodeQL): the image-URL preview is guarded to http(s) only; the fallback is a static data-URI. Non-required check.
    Earlier-run threads are superseded by these commits.

@LucasSantana-Dev
LucasSantana-Dev dismissed coderabbitai[bot]’s stale review June 23, 2026 13:57

Addressed: SSRF/S5693/lint fixed; roleId route-validated; threads resolved.

@sonarqubecloud

Copy link
Copy Markdown

@LucasSantana-Dev
LucasSantana-Dev merged commit ac5e0e9 into main Jun 23, 2026
44 of 46 checks passed
@LucasSantana-Dev
LucasSantana-Dev deleted the feat/reaction-roles-form-overhaul branch June 23, 2026 13:59
@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{"name":"HttpError","status":500,"request":{"method":"PATCH","url":"https://api.github.com/repos/LucasSantana-Dev/Lucky/issues/comments/4778686378","headers":{"accept":"application/vnd.github.v3+json","user-agent":"octokit.js/0.0.0-development octokit-core.js/7.0.6 Node.js/24","authorization":"token [REDACTED]","content-type":"application/json; charset=utf-8"},"body":{"body":"<!-- This is an auto-generated comment: summarize by coderabbit.ai -->\n<!-- review_stack_entry_start -->\n\n[![Review Change Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/LucasSantana-Dev/Lucky/pull/1544?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)\n\n<!-- review_stack_entry_end -->\n<!-- This is an auto-generated comment: review in progress by coderabbit.ai -->\n\n> [!NOTE]\n> Currently processing new changes in this PR. This may take a few minutes, please wait...\n> \n> <details>\n> <summary>⚙️ Run configuration</summary>\n> \n> **Configuration used**: Organization UI\n> \n> **Review profile**: CHILL\n> \n> **Plan**: Pro\n> \n> **Run ID**: `b7be9307-3f9e-4d64-bd0d-e226fd25bd5a`\n> \n> </details>\n> \n> <details>\n> <summary>📥 Commits</summary>\n> \n> Reviewing files that changed from the base of the PR and between b33c609832bdba7b9006995de445796eb02f248c and 7a26932c2e03faeea14a9178d35bedf519c7858c.\n> \n> </details>\n> \n> <details>\n> <summary>📒 Files selected for processing (8)</summary>\n> \n> * `packages/backend/src/errors/AppError.ts`\n> * `packages/backend/src/routes/roles.ts`\n> * `packages/backend/src/services/GuildService.ts`\n> * `packages/backend/tests/integration/routes/roles.test.ts`\n> * `packages/frontend/src/pages/ReactionRoles.test.tsx`\n> * `packages/frontend/src/pages/ReactionRoles.tsx`\n> * `packages/shared/src/services/ReactionRolesService/index.ts`\n> * `sonar-project.properties`\n> \n> </details>\n> \n> \n\n<!-- end of auto-generated comment: review in progress by coderabbit.ai -->\n\n<!-- walkthrough_start -->\n\n<details>\n<summary>📝 Walkthrough</summary>\n\n## Walkthrough\n\nAdds full edit support for reaction-role dashboard messages, image upload via multipart form data, embed fields (`title`, `description`, `imageUrl`) persisted in a new Prisma migration, a `GuildService.getGuildEmojis` endpoint with bot-client/REST fallback, export/import JSON utilities, and a reworked React page with `EmojiPicker`, `AutoGrowTextarea`, `FormattingToolbar`, and `ImportDialog` components.\n\n## Changes\n\n**Reaction Roles Dashboard Enhancement**\n\n|Layer / File(s)|Summary|\n|---|---|\n|**DB schema, Prisma migration, and shared data contracts** <br> `prisma/migrations/20260623065023_add_reaction_role_embed_content/migration.sql`, `prisma/schema.prisma`, `packages/shared/src/services/ReactionRolesService/index.ts`, `packages/frontend/src/types/guild.ts`, `packages/frontend/src/services/reactionRolesApi.ts`, `packages/backend/src/schemas/management.ts`, `packages/backend/src/services/GuildService.ts`|Adds nullable `title`, `description`, and `imageUrl` columns to `reaction_role_messages` via migration and Prisma schema. Defines `DashboardUpdateReactionRoleOptions`, `GuildEmojiOption`, and `UpdateReactionRolePayload` interfaces. Extends `ReactionRoleMessage` and `CreateReactionRolePayload` types. Adds `imageUrl` field validation to management Zod schemas.|\n|**GuildService.getGuildEmojis and API endpoint** <br> `packages/backend/src/services/GuildService.ts`, `packages/backend/src/routes/guilds.ts`, `packages/frontend/src/services/api.ts`, `packages/backend/tests/unit/services/GuildService.emojis.test.ts`|Implements `getGuildEmojis` with bot-client emoji cache path and Discord REST fallback (10s timeout, `AbortSignal`). Adds `GET /api/guilds/:guildId/emojis` route with `requireGuildModuleAccess('overview')` check. Wires `api.guilds.getEmojis` on frontend. Unit tests cover bot-client resolution, REST fallback, filtering, error propagation, network failures, and empty-cache cases.|\n|**ReactionRolesService: updateReactionRoleMessage and createFromDashboard refactor** <br> `packages/shared/src/services/ReactionRolesService/index.ts`, `packages/shared/src/services/ReactionRolesService/index.spec.ts`|Extracts `assertSnowflakes` and `buildButtonRows` helpers for ID validation and button payload construction. Refactors `createReactionRoleMessageFromDashboard` to use them and support multipart/JSON image posting. Adds `updateReactionRoleMessage` implementing PATCH flow: validates ownership, rebuilds button rows, PATCHes Discord with optional `FormData`, and updates mappings via Prisma `$transaction`. Spec coverage updated to use valid snowflake role IDs; extensive tests for edit, image, and multipart upload flows.|\n|**Backend routes: multer middleware, POST/PUT handlers, error mapping** <br> `packages/backend/src/routes/roles.ts`, `packages/backend/src/errors/AppError.ts`, `packages/backend/tests/integration/routes/roles.test.ts`|Adds multer in-memory upload middleware (8 MB, PNG/JPEG/GIF/WebP, bounds multipart parts/fields) with `imageUploadHandler` wrapper converting upload errors to `AppError` (413 for size, 400 for type). Refactors `POST` and `PUT` reaction-roles routes to parse multipart/JSON, validate via `safeParse`, construct optional `imageFile`, require `DISCORD_TOKEN`, and call service methods with mode-specific error mapping. Adds `AppError.badGateway` factory. Extends role management endpoints to map Discord API/bot token errors to bad-gateway. Integration tests cover multipart upload success/failure, JSON-only fallback, and update-with-file scenarios.|\n|**Frontend API: reactionRolesApi FormData support** <br> `packages/frontend/src/services/reactionRolesApi.ts`, `packages/frontend/src/services/reactionRolesApi.test.ts`|`create` and `update` accept optional `imageFile?: File` and switch to multipart `FormData` when provided. Tests verify `FormData` structure (`image`, JSON-stringified `payload`) for both methods.|\n|**Export/import utilities** <br> `packages/frontend/src/utils/reactionRolesExport.ts`, `packages/frontend/src/utils/reactionRolesExport.test.ts`|`serializeReactionRolesToJSON` maps `ReactionRoleMessage[]` to `ExportedReactionRole[]`, omitting identifying metadata. `deserializeReactionRolesJSON` parses and validates JSON with snowflake format, length, role count, and style constraints, returning a `DeserializeResult` with accumulated errors. Tests cover serialization omissions and comprehensive deserialization error/success cases.|\n|**New UI components: AutoGrowTextarea, FormattingToolbar, EmojiPicker, ImportDialog** <br> `packages/frontend/src/components/ui/AutoGrowTextarea.tsx`, `packages/frontend/src/components/ui/FormattingToolbar.tsx`, `packages/frontend/src/components/ui/EmojiPicker.tsx`, `packages/frontend/src/components/reactionRoles/ImportDialog.tsx`, `packages/frontend/src/components/{ui,reactionRoles}/*.test.tsx`|`AutoGrowTextarea` auto-adjusts height between `minRows`/`maxRows` via `scrollHeight` calculation. `FormattingToolbar` wraps or inserts markdown-like markers (bold, italic, underline, strikethrough, spoiler) via a textarea ref. `EmojiPicker` displays tabbed standard and server emoji dropdown, lazily fetching server emojis from guild emoji API with image error fallback. `ImportDialog` handles JSON paste/file import with per-item sequential API calls, progress updates, and per-index error collection. Comprehensive component tests verify rendering, interaction, state management, and error paths.|\n|**ReactionRoles page overhaul: unified MessageForm with edit/create/image/export/import** <br> `packages/frontend/src/pages/ReactionRoles.tsx`, `packages/frontend/src/pages/ReactionRoles.test.tsx`|Replaces create-only dialog with unified `MessageForm` supporting create/edit modes. In edit mode, pre-fills all fields from existing message and disables channel selection. Adds image URL input with preview and file upload with preview. Reworks role mappings into card-based editors with `EmojiPicker`, `FormattingToolbar`, `AutoGrowTextarea` for description, and enforced max 25 roles. Form submission branches on mode: create calls `api.reactionRoles.create`, edit calls `api.reactionRoles.update`. Implements Export (JSON download) and Import (opens `ImportDialog`) header actions. `MessageCard` gains `onEdit` callback and Edit button. Page manages unified form/import state. Comprehensive tests cover edit/create dialog flows, image URL/file upload, auto-grow textarea, export state and download, import validation/success/partial-failure, form validation, role constraints, and payload branching for image URL vs file paths.|\n|**SonarQube configuration** <br> `sonar-project.properties`|Ignores `typescript:S5693` (request content-length limit) for reaction-roles image upload routes in `packages/backend/src/routes/roles.ts`.|\n\n## Sequence Diagram(s)\n\n```mermaid\nsequenceDiagram\n  participant User\n  participant ReactionRolesPage\n  participant MessageForm\n  participant ReactionRolesApi\n  participant RolesRoute\n  participant ReactionRolesService\n  participant DiscordApi\n  participant PrismaDb\n\n  User->>ReactionRolesPage: Click Edit on message\n  ReactionRolesPage->>MessageForm: mode=edit, initialMessage\n  MessageForm->>MessageForm: pre-fill fields\n  User->>MessageForm: Select image file\n  User->>MessageForm: Click Update\n  MessageForm->>ReactionRolesApi: update(guildId, messageId, payload, imageFile)\n  ReactionRolesApi->>RolesRoute: PUT multipart FormData\n  RolesRoute->>ReactionRolesService: updateReactionRoleMessage(options)\n  ReactionRolesService->>DiscordApi: PATCH /channels/{id}/messages/{id}\n  DiscordApi-->>ReactionRolesService: updated message\n  ReactionRolesService->>PrismaDb: $transaction(delete mappings, update message + imageUrl)\n  PrismaDb-->>ReactionRolesService: committed\n  ReactionRolesService-->>RolesRoute: { messageId }\n  RolesRoute-->>ReactionRolesApi: 200 response\n  ReactionRolesApi-->>MessageForm: success\n  MessageForm->>ReactionRolesPage: onSuccess\n  ReactionRolesPage->>ReactionRolesPage: refresh messages\n```\n\n```mermaid\nsequenceDiagram\n  participant User\n  participant ImportDialog\n  participant ExportDeserializer\n  participant ReactionRolesApi\n  participant RolesRoute\n\n  User->>ImportDialog: Submit pasted JSON\n  ImportDialog->>ExportDeserializer: deserializeReactionRolesJSON(json)\n  alt valid payload list\n    loop each item\n      ImportDialog->>ReactionRolesApi: create(guildId, item)\n      ReactionRolesApi->>RolesRoute: POST reaction role\n      RolesRoute-->>ReactionRolesApi: success or error\n      ReactionRolesApi-->>ImportDialog: per-item result\n    end\n  else invalid payload\n    ExportDeserializer-->>ImportDialog: validation errors\n  end\n  ImportDialog-->>User: progress and result summary\n```\n\n## Estimated code review effort\n\n🎯 5 (Critical) | ⏱️ ~120 minutes\n\n## Possibly related issues\n\n- **`#1540`** — The PR adds `assertSnowflakes` validation for `roleId` in the updated `buildButtonRows` and new `updateReactionRoleMessage` methods, alongside Discord API response validation in `createReactionRoleMessageFromDashboard`, directly addressing the service hardening and ID validation concerns in that issue.\n\n## Possibly related PRs\n\n- [LucasSantana-Dev/Lucky#1532](https://github.com/LucasSantana-Dev/Lucky/pull/1532): This PR directly extends the reaction-roles dashboard REST POST/PUT endpoints and `ReactionRolesService` introduced in that PR by adding image upload, edit support (`updateReactionRoleMessage`), and embed field persistence.\n- [LucasSantana-Dev/Lucky#392](https://github.com/LucasSantana-Dev/Lucky/pull/392): Both PRs modify `packages/frontend/src/pages/ReactionRoles.test.tsx` with substantial test additions for Reaction Roles UI rendering and form/dialog interaction flows at the same file level.\n- [LucasSantana-Dev/Lucky#165](https://github.com/LucasSantana-Dev/Lucky/pull/165): The new GET /api/guilds/:guildId/emojis route uses `requireGuildModuleAccess('overview')`, which is part of the RBAC access control framework introduced in that PR.\n\n</details>\n\n<!-- walkthrough_end -->\n<!-- pre_merge_checks_walkthrough_start -->\n\n<details>\n<summary>🚥 Pre-merge checks | ✅ 4 | ❌ 1</summary>\n\n### ❌ Failed checks (1 warning)\n\n|     Check name     | Status     | Explanation                                                                          | Resolution                                                                         |\n| :----------------: | :--------- | :----------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------- |\n| Docstring Coverage | ⚠️ Warning | Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. | Write docstrings for the functions missing them to satisfy the coverage threshold. |\n\n<details>\n<summary>✅ Passed checks (4 passed)</summary>\n\n|         Check name         | Status   | Explanation                                                                                                                                                                                                        |\n| :------------------------: | :------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n|         Title check        | ✅ Passed | The title accurately and concisely summarizes the main changes: a comprehensive overhaul of the Reaction Roles dashboard form adding editing, emoji picker, formatting, media support, and export/import features. |\n|     Linked Issues check    | ✅ Passed | Check skipped because no linked issues were found for this pull request.                                                                                                                                           |\n| Out of Scope Changes check | ✅ Passed | Check skipped because no linked issues were found for this pull request.                                                                                                                                           |\n|      Description Check     | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled.                                                                                                                                                        |\n\n</details>\n\n<sub>✏️ Tip: You can configure your own custom pre-merge checks in the settings.</sub>\n\n</details>\n\n<!-- pre_merge_checks_walkthrough_end -->\n<!-- finishing_touch_checkbox_start -->\n\n<details>\n<summary>✨ Finishing Touches</summary>\n\n<details>\n<summary>📝 Generate docstrings</summary>\n\n- [ ] <!-- {\"checkboxId\": \"7962f53c-55bc-4827-bfbf-6a18da830691\"} --> Create stacked PR\n- [ ] <!-- {\"checkboxId\": \"3e1879ae-f29b-4d0d-8e06-d12b7ba33d98\"} --> Commit on current branch\n\n</details>\n<details>\n<summary>🧪 Generate unit tests (beta)</summary>\n\n- [ ] <!-- {\"checkboxId\": \"f47ac10b-58cc-4372-a567-0e02b2c3d479\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Create PR with unit tests\n- [ ] <!-- {\"checkboxId\": \"6ba7b810-9dad-11d1-80b4-00c04fd430c8\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Commit unit tests in branch `feat/reaction-roles-form-overhaul`\n\n</details>\n\n</details>\n\n<!-- finishing_touch_checkbox_end -->\n<!-- tips_start -->\n\n---\n\nThanks for using [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=LucasSantana-Dev/Lucky&utm_content=1544)! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.\n\n<details>\n<summary>❤️ Share</summary>\n\n- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)\n- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)\n- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)\n- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)\n\n</details>\n\n\n<sub>Comment `@coderabbitai help` to get the list of available commands.</sub>\n\n<!-- tips_end -->"},"request":{"retryCount":3,"signal":{},"retries":3,"retryAfter":16}}}

LucasSantana-Dev added a commit that referenced this pull request Jun 23, 2026
🤖 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. -->
LucasSantana-Dev added a commit that referenced this pull request Jun 23, 2026
## 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 -->
LucasSantana-Dev added a commit that referenced this pull request Jul 9, 2026
: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 -->

This branch was successfully deployed

1 active deployment
Preview — 7a26932c Deployed Jun 23, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants