Skip to content

feat(role-groups): composite add-styled-role v1 - #1557

Merged
LucasSantana-Dev merged 13 commits into
mainfrom
feat/role-groups
Jun 23, 2026
Merged

LucasSantana-Dev merged 13 commits into
mainfrom
feat/role-groups

Conversation

@LucasSantana-Dev

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

Copy link
Copy Markdown
Owner

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)

Verification

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


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.

Written for commit abdb6c1. Summary will update on new commits.

Review in cubic

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.

First-class RoleGroup entity (1:1 with one reaction-role message) + one
composite endpoint that creates a styled role, attaches it, and adds the
button in one dashboard action with a dry-run preview.

Decided via /brainstorming -> /deep-research -> /grill-with-docs (6
doc-grounded skeptics). Key calls: DB-first append + compensation (defer
full idempotency to v2, sidesteps the #1555 orphaned-button risk); color
as hex String; buttonStyle as String; DB-level unique(groupId); drop the
requester-hierarchy check (new roles created at position 0).

Local design: .claude/plans/2026-06-23-role-groups-design.md
New RoleGroup model (table role_groups) wrapping one reaction-role message
with an editable style template (color hex string, hoist, mentionable,
buttonStyle, defaultEmoji). ReactionRoleMessage gains a nullable groupId FK
(onDelete SetNull) + @@unique([groupId]) for v1 1:1 (Postgres treats NULLs
as distinct, so ungrouped messages are unconstrained).

Additive, no backfill. Migration hand-authored to match Prisma DDL (no local
DB); prisma-migration-verifier PASS; type:check green across all packages.

Ref: decisions/2026-06-23-role-groups-composite-actions.md
New ReactionRolesService.addRoleToMessage(): appends one role/button to an
existing reaction-role message DB-first — inserts a single ReactionRoleMapping
in a Prisma transaction BEFORE the Discord PATCH, then re-renders the full
button set. No deleteMany (append-only). Guards: 25-button capacity, duplicate
roleId. On Discord PATCH failure after the insert, returns partial_success
(DB stays correct, visuals re-sync on next render) instead of orphaning
buttons like the legacy update path (ref #1555). updateReactionRoleMessage
untouched.

TDD: 6 behavior tests (ordering, partial_success, guards, no-deleteMany).
Shared suite 1286 passing; type:check green.

Ref: decisions/2026-06-23-role-groups-composite-actions.md
Implements the composite executor for add-styled-role feature:
- seedStyleFromMessage: modal color + mode buttonStyle + divergence
- createRoleGroup: from-message (seeded + linked) and standalone
- addRoleToGroup: preflights, dryRun plan, DB-first apply,
  compensation, partial_success audit

Preflight guards: Discord/UX limits (25 buttons, 250 roles, 80 label),
double-submit prevention via name uniqueness check.

Compensation: deleteGuildRole on mapping failure (404-tolerant),
partial_success audit on Discord PATCH failure.

Color handling: hex <-> int 2-way conversion helpers with null safety.

21 passing unit tests (mocked guildService, reactionRolesService, prisma).
Backend type:check green. No regressions in 1178 existing tests.
Routes /api/guilds/:guildId/role-groups (settings:manage gated): create
(from-message or standalone), list, detail, patch template, the composite
POST /:id/roles (dry-run + apply), and DELETE /:id/roles/:roleId (detach,
keep the Discord role). Zod validation (color ^0x[0-9A-Fa-f]{6}$, label <=80).
RoleGroupService guard throws refactored to AppError so the error middleware
maps not-found->404, conflict/capacity/duplicate->409, bad input->400; added
AppError.conflict() and service methods listRoleGroups/getRoleGroup/
updateRoleGroup/detachRoleFromGroup.

Fixed a regression the refactor introduced: the service had switched the
reactionRolesService import to the barrel while the unit test mocked the deep
path (undefined -> 4 failing apply-path tests); realigned to the deep path.

TDD: 22 integration tests; full backend suite 1200 passing; type:check green.

Ref: decisions/2026-06-23-role-groups-composite-actions.md
New roleGroupsApi client (list/create/get/updateTemplate/addRole/detachRole,
addRole supports dryRun) registered on the shared api. New AddStyledRoleForm
component: three-phase UX (form -> dry-run preview with color swatch ->
applied result), double-submit guards, distinct partial_success "will
re-sync" message, reuses the existing EmojiPicker/Button/Card.

TDD: 18 tests (7 api + 11 component); frontend suite 872 passing; type:check
+ lint clean. Component-test-verified only (dashboard is auth-gated).

NOTE: the form is not yet mounted into ReactionRoles.tsx — page wiring +
create-group affordance follow in phase 4b.

Ref: decisions/2026-06-23-role-groups-composite-actions.md
Wires the feature end-to-end in ReactionRoles.tsx: ungrouped messages show a
"Create role group" button (api.roleGroups.create with the message cuid id ->
refetch); grouped messages show an "Add styled role" disclosure that mounts
AddStyledRoleForm (guildId + groupId, onSuccess -> refetch). Added groupId to
the frontend ReactionRoleMessage type (nullable).

Replaced a vacuous placeholder test (expect(container).toBeTruthy()) with a
real assertion: clicking the toggle on a grouped message mounts the form
(Preview button + Role Name field) and the create-group button is absent.

Frontend suite 876 passing; type:check + lint (--max-warnings 0) clean.
Wiring-test-verified only — dashboard is auth-gated; visual verification is
a phase-5 human step.

Ref: decisions/2026-06-23-role-groups-composite-actions.md
@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 11:19pm

@coderabbitai

coderabbitai Bot commented Jun 23, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds "Role Groups" as a first-class entity: a new RoleGroup Prisma model links 1:1 to a ReactionRoleMessage via a nullable groupId FK. A shared addRoleToMessage method implements DB-first role creation with Discord compensation and partial_success handling. A backend RoleGroupService provides style derivation, CRUD, and compensating deletion on partial failure. REST endpoints and a two-step React form (dry-run preview → confirm) complete the feature, with comprehensive tests covering IDOR checks, tie-breaking, transactional atomicity, and partial-success flows.

Changes

Role Groups Feature

Layer / File(s) Summary
DB schema and migration
prisma/schema.prisma, prisma/migrations/20260623120000_add_role_groups/migration.sql
New RoleGroup model, nullable groupId FK on ReactionRoleMessage with a unique index enforcing 1:1 semantics, Guild.roleGroups relation, and SQL migration with cascade/set-null FK constraints and no backfill.
ReactionRolesService.addRoleToMessage (DB-first append)
packages/shared/src/services/ReactionRolesService/index.ts, packages/shared/src/services/ReactionRolesService/index.spec.ts
New DB-first append method that validates capacity and duplicate roleId, inserts via Prisma transaction, PATCHes Discord, and returns ok or partial_success; tests verify ordering (DB before PATCH), partial failure handling, and validation rejection paths.
Backend contracts: AppError and Zod schemas
packages/backend/src/errors/AppError.ts, packages/backend/src/schemas/management.ts
AppError.conflict(409) factory method added; managementSchemas extended with roleGroupIdParam, roleGroupRoleIdParam, createRoleGroupBody, updateRoleGroupBody, and addRoleToGroupBody Zod schemas.
RoleGroupService: style utilities, CRUD, and addRoleToGroup
packages/backend/src/services/RoleGroupService.ts, packages/backend/tests/unit/services/RoleGroupService.test.ts
RoleGroupService with hexToInt/intToHex color conversion, seedStyleFromMessage (modal color/hoist/buttonStyle derivation with tie-breaking), createRoleGroup (1:1 conflict guard with atomic transactional linking and style seeding), addRoleToGroup (preflight checks for Discord/DB limits, dry-run plan generation, DB-first apply with Discord role creation and compensation deletion on failure), and CRUD methods; unit tests cover IDOR checks, tie-breaking, transactional atomicity, compensation, and partial-success.
Backend routes: registration and handlers
packages/backend/src/routes/roleGroups.ts, packages/backend/src/routes/index.ts, packages/backend/tests/integration/routes/roleGroups.test.ts, packages/backend/tests/unit/routes/index.test.ts
setupRoleGroupsRoutes registers POST/GET/PATCH/DELETE endpoints under /api/guilds/:guildId/role-groups with validation and error mapping; routes/index.ts adds guildGuardConfigs entry with settings:manage permission guard and appends to routeSetups; integration and unit tests validate route mounting, HTTP status codes, and all error/conflict paths.
Frontend API types, client factory, and wiring
packages/frontend/src/services/roleGroupsApi.ts, packages/frontend/src/services/api.ts, packages/frontend/src/services/reactionRolesApi.ts, packages/frontend/src/services/roleGroupsApi.test.ts
createRoleGroupsApi factory exports typed interfaces for payloads and results (RoleGroup, CreateRoleGroupPayload, AddRoleDryRunResult, AddRoleAppliedResult) and provides async methods (list, create, get, updateTemplate, addRole, detachRole); wired into api.roleGroups; ReactionRoleMessage gains optional groupId field; Vitest suite validates all HTTP methods and payloads.
AddStyledRoleForm component and ReactionRoles page integration
packages/frontend/src/components/reactionRoles/AddStyledRoleForm.tsx, packages/frontend/src/components/reactionRoles/AddStyledRoleForm.test.tsx, packages/frontend/src/pages/ReactionRoles.tsx, packages/frontend/src/pages/ReactionRoles.test.tsx, packages/frontend/src/locales/*.json
AddStyledRoleForm implements two-step flow (dry-run preview with dryRun: true → confirm apply with dryRun: false) and displays partial-success notice when Discord PATCH fails; ReactionRoles/MessageCard adds handleCreateRoleGroup handler, conditional "Create role group" button for ungrouped messages and "Add styled role" toggle for grouped, and mounts the form; tests cover all UI states, error handling, double-submit guard, and partial-success; locale strings added for group creation labels and error messages.
ADR: Role Groups Composite Actions & Concurrency Hardening
decisions/2026-06-23-role-groups-composite-actions.md
Decision document defines v2.21.0+ shipped scope (IDOR/guild checks, schema fixes, tie-breaking, type-safety, test coverage, UX retry clearing, atomic linking via transaction/race detection) and defers v2.2 concurrency guard for addRoleToMessage appends with known failure mode (>25 buttons) and proposed transactional fix.

Sequence Diagram

sequenceDiagram
  participant User
  participant AddStyledRoleForm
  participant api.roleGroups
  participant RoleGroupService
  participant GuildService
  participant ReactionRolesService
  participant Discord

  rect rgba(100, 149, 237, 0.5)
    note over User,AddStyledRoleForm: Dry-run preview step
    User->>AddStyledRoleForm: Fill name, label, color → click Preview
    AddStyledRoleForm->>api.roleGroups: addRole(guildId, groupId, { dryRun: true })
    api.roleGroups->>RoleGroupService: POST .../roles (dryRun: true)
    RoleGroupService->>RoleGroupService: preflight checks (capacity, label, duplicate)
    RoleGroupService-->>api.roleGroups: AddRoleDryRunResult (plan)
    api.roleGroups-->>AddStyledRoleForm: plan rendered
  end

  rect rgba(60, 179, 113, 0.5)
    note over User,Discord: Apply/confirm step
    User->>AddStyledRoleForm: click Confirm
    AddStyledRoleForm->>api.roleGroups: addRole(guildId, groupId, { dryRun: false })
    api.roleGroups->>RoleGroupService: POST .../roles (dryRun: false)
    RoleGroupService->>GuildService: createGuildRole(guildId, roleData)
    GuildService->>Discord: POST /guilds/{id}/roles
    Discord-->>GuildService: created role
    RoleGroupService->>ReactionRolesService: addRoleToMessage(messageId, mapping, token)
    ReactionRolesService->>Discord: PATCH message (full button set)
    alt Discord PATCH ok
      ReactionRolesService-->>RoleGroupService: { status: 'ok', mapping }
      RoleGroupService-->>AddStyledRoleForm: AddRoleAppliedResult { status: 'ok' }
    else Discord PATCH fails
      ReactionRolesService-->>RoleGroupService: { status: 'partial_success', mapping }
      RoleGroupService-->>AddStyledRoleForm: { status: 'partial_success' }
      AddStyledRoleForm-->>User: "will re-sync shortly" notice
    end
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

  • LucasSantana-Dev/Lucky#1555: The new addRoleToMessage DB-first approach with partial_success handling and Discord role compensation directly address the transaction atomicity and consistency concerns raised in this issue.

Possibly related PRs

  • LucasSantana-Dev/Lucky#392: Both PRs extend packages/frontend/src/pages/ReactionRoles.test.tsx, adding test coverage for role-group integration flows and ReactionRoles page functionality respectively.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
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.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(role-groups): composite add-styled-role v1' directly and accurately describes the main feature introduced in this changeset—a composite role-group feature enabling styled role addition with a v1 implementation.
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ 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/role-groups

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.

@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 — 4233 lines changed across 24 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.)

⚠️

This PR appears to remove a feature or route (detected in commit message). Please fill in the Feature-removal sweep checklist in the PR template to ensure no orphan code (models, tests, types, imports) is left behind. See decisions/ for context.

⚠️

New file packages/backend/src/services/RoleGroupService.ts is 509 lines — consider splitting.

⚠️

New file packages/backend/tests/integration/routes/roleGroups.test.ts is 550 lines — consider splitting.

⚠️

New file packages/backend/tests/unit/services/RoleGroupService.test.ts is 839 lines — consider splitting.

Generated by 🚫 dangerJS against abdb6c1

Comment thread packages/backend/src/routes/roleGroups.ts Fixed
@coderabbitai coderabbitai Bot added the enhancement New feature or request label Jun 23, 2026
@github-actions

github-actions Bot commented Jun 23, 2026 •

Copy link
Copy Markdown

Size Change: +1.62 kB (+0.35%)

Total Size: 468 kB

📦 View Changed
Filename Size Change
packages/frontend/dist/assets/Admin-C1JR0Zal.js 2.3 kB +2.3 kB (new file) 🆕
packages/frontend/dist/assets/Admin-CLjd_fq3.js 0 B -2.29 kB (removed) 🏆
packages/frontend/dist/assets/AdminSupport-DKJoSERo.js 0 B -1.59 kB (removed) 🏆
packages/frontend/dist/assets/AdminSupport-Hx0DXxcD.js 1.6 kB +1.6 kB (new file) 🆕
packages/frontend/dist/assets/api-BcNJTia0.js 3.87 kB +3.87 kB (new file) 🆕
packages/frontend/dist/assets/api-IIHeW0Kf.js 0 B -3.78 kB (removed) 🏆
packages/frontend/dist/assets/AutoMessages-ChhSsB_B.js 2.65 kB +2.65 kB (new file) 🆕
packages/frontend/dist/assets/AutoMessages-PROf4e1_.js 0 B -2.65 kB (removed) 🏆
packages/frontend/dist/assets/AutoMod-B7OqzEgX.js 4.19 kB +4.19 kB (new file) 🆕
packages/frontend/dist/assets/AutoMod-BLdCb-J2.js 0 B -4.19 kB (removed) 🏆
packages/frontend/dist/assets/badge-CnpjZ1-M.js 0 B -499 B (removed) 🏆
packages/frontend/dist/assets/badge-DXhpF_sM.js 504 B +504 B (new file) 🆕
packages/frontend/dist/assets/Card-7IylI-Es.js 506 B +506 B (new file) 🆕
packages/frontend/dist/assets/Card-BU-cxB-V.js 0 B -505 B (removed) 🏆
packages/frontend/dist/assets/Changelog-B42CP6V8.js 38.8 kB +38.8 kB (new file) 🆕
packages/frontend/dist/assets/Changelog-BcK3F5pc.js 0 B -38.8 kB (removed) 🏆
packages/frontend/dist/assets/CommandsConfig-BcGHDkM9.js 1.5 kB +1.5 kB (new file) 🆕
packages/frontend/dist/assets/CommandsConfig-oMWuestg.js 0 B -1.5 kB (removed) 🏆
packages/frontend/dist/assets/Config-CulurV1Z.js 0 B -1.97 kB (removed) 🏆
packages/frontend/dist/assets/Config-DJKOjaK-.js 1.98 kB +1.98 kB (new file) 🆕
packages/frontend/dist/assets/CustomCommands-BayR1LDJ.js 0 B -2.12 kB (removed) 🏆
packages/frontend/dist/assets/CustomCommands-C5Ir4UNr.js 2.12 kB +2.12 kB (new file) 🆕
packages/frontend/dist/assets/DashboardOverview-C_aU8Ay5.js 0 B -3.96 kB (removed) 🏆
packages/frontend/dist/assets/DashboardOverview-lRZlZOZJ.js 3.96 kB +3.96 kB (new file) 🆕
packages/frontend/dist/assets/dialog-DnhSrbDz.js 0 B -953 B (removed) 🏆
packages/frontend/dist/assets/dialog-tOlmfo0b.js 955 B +955 B (new file) 🆕
packages/frontend/dist/assets/Docs-BsmzdGSY.js 17.6 kB +17.6 kB (new file) 🆕
packages/frontend/dist/assets/Docs-CcoL8sAv.js 0 B -17.6 kB (removed) 🏆
packages/frontend/dist/assets/DocsShell-CBs5PNgb.js 1.42 kB +1.42 kB (new file) 🆕
packages/frontend/dist/assets/DocsShell-CIPyJyZF.js 0 B -1.42 kB (removed) 🏆
packages/frontend/dist/assets/EmbedBuilder-CMT3vInP.js 3.28 kB +3.28 kB (new file) 🆕
packages/frontend/dist/assets/EmbedBuilder-D-qePI0Y.js 0 B -3.28 kB (removed) 🏆
packages/frontend/dist/assets/Features-ChWOcDxF.js 0 B -754 B (removed) 🏆
packages/frontend/dist/assets/Features-rv4tQ2MP.js 756 B +756 B (new file) 🆕
packages/frontend/dist/assets/GuildAutomation-BAOneE6T.js 0 B -2.88 kB (removed) 🏆
packages/frontend/dist/assets/GuildAutomation-DdgTRyvZ.js 2.89 kB +2.89 kB (new file) 🆕
packages/frontend/dist/assets/index-BIxTlu38.js 70.2 kB +70.2 kB (new file) 🆕
packages/frontend/dist/assets/index-DqH0fjbU.js 0 B -70.1 kB (removed) 🏆
packages/frontend/dist/assets/input--jWWUfNB.js 464 B +464 B (new file) 🆕
packages/frontend/dist/assets/input-DdtgB8rT.js 0 B -462 B (removed) 🏆
packages/frontend/dist/assets/label-DZCixodi.js 0 B -474 B (removed) 🏆
packages/frontend/dist/assets/label-srAHHY_O.js 479 B +479 B (new file) 🆕
packages/frontend/dist/assets/Landing-BKYFEgG3.js 5.2 kB +5.2 kB (new file) 🆕
packages/frontend/dist/assets/Landing-eOZxZz6F.js 0 B -5.2 kB (removed) 🏆
packages/frontend/dist/assets/LastFm-DP6mcfYT.js 0 B -1.74 kB (removed) 🏆
packages/frontend/dist/assets/LastFm-Dz3qyEY_.js 1.74 kB +1.74 kB (new file) 🆕
packages/frontend/dist/assets/Levels-BTNfRm-i.js 0 B -2.26 kB (removed) 🏆
packages/frontend/dist/assets/Levels-DuEHviFP.js 2.27 kB +2.27 kB (new file) 🆕
packages/frontend/dist/assets/Login-BepK9urT.js 2.49 kB +2.49 kB (new file) 🆕
packages/frontend/dist/assets/Login-CYtGpoeJ.js 0 B -2.49 kB (removed) 🏆
packages/frontend/dist/assets/Lyrics-BFc4iB0Y.js 0 B -1.34 kB (removed) 🏆
packages/frontend/dist/assets/Lyrics-BHXc8Qom.js 1.34 kB +1.34 kB (new file) 🆕
packages/frontend/dist/assets/Moderation-CpqbSAm0.js 0 B -3.79 kB (removed) 🏆
packages/frontend/dist/assets/Moderation-D2EExhfM.js 3.79 kB +3.79 kB (new file) 🆕
packages/frontend/dist/assets/Music-BIO5ZKLE.js 5.96 kB +5.96 kB (new file) 🆕
packages/frontend/dist/assets/Music-DLxSqpMh.js 0 B -5.95 kB (removed) 🏆
packages/frontend/dist/assets/MusicConfig-Bypqfvip.js 0 B -1.64 kB (removed) 🏆
packages/frontend/dist/assets/MusicConfig-CggtwBku.js 1.64 kB +1.64 kB (new file) 🆕
packages/frontend/dist/assets/PreferredArtists-C3TTffJx.js 0 B -3.72 kB (removed) 🏆
packages/frontend/dist/assets/PreferredArtists-mTHCjBSt.js 3.72 kB +3.72 kB (new file) 🆕
packages/frontend/dist/assets/PrivacyPolicy-Dv5Ft3dv.js 1.77 kB +1.77 kB (new file) 🆕
packages/frontend/dist/assets/PrivacyPolicy-s5Hl-wHH.js 0 B -1.77 kB (removed) 🏆
packages/frontend/dist/assets/ReactionRoles-Bngco6Be.js 0 B -7.92 kB (removed) 🏆
packages/frontend/dist/assets/ReactionRoles-D8MwPl5z.js 9.01 kB +9.01 kB (new file) 🆕
packages/frontend/dist/assets/Roles-BIA-xgHr.js 0 B -3.34 kB (removed) 🏆
packages/frontend/dist/assets/Roles-BjdcG1mz.js 3.34 kB +3.34 kB (new file) 🆕
packages/frontend/dist/assets/SectionHeader-C6Fj4j8w.js 0 B -893 B (removed) 🏆
packages/frontend/dist/assets/SectionHeader-D_z7SeUf.js 894 B +894 B (new file) 🆕
packages/frontend/dist/assets/select-DEK-N-Yt.js 0 B -1.22 kB (removed) 🏆
packages/frontend/dist/assets/select-DHbJvMfE.js 1.23 kB +1.23 kB (new file) 🆕
packages/frontend/dist/assets/ServerLogs-BwlbtST6.js 3.04 kB +3.04 kB (new file) 🆕
packages/frontend/dist/assets/ServerLogs-DXFqibcC.js 0 B -3.04 kB (removed) 🏆
packages/frontend/dist/assets/ServerSettings-D-czTcBA.js 3.99 kB +3.99 kB (new file) 🆕
packages/frontend/dist/assets/ServerSettings-oBgu8Jl3.js 0 B -3.98 kB (removed) 🏆
packages/frontend/dist/assets/ServersPage-DE6UCPkG.js 0 B -3.04 kB (removed) 🏆
packages/frontend/dist/assets/ServersPage-scDDKapV.js 3.04 kB +3.04 kB (new file) 🆕
packages/frontend/dist/assets/Skeleton-CNaYaDPd.js 0 B -232 B (removed) 🏆
packages/frontend/dist/assets/Skeleton-EdYWO-6L.js 236 B +236 B (new file) 🆕
packages/frontend/dist/assets/Spotify-BmFHVRy8.js 1.75 kB +1.75 kB (new file) 🆕
packages/frontend/dist/assets/Spotify-CiPoheNk.js 0 B -1.75 kB (removed) 🏆
packages/frontend/dist/assets/Starboard-C2I3nAW6.js 0 B -1.82 kB (removed) 🏆
packages/frontend/dist/assets/Starboard-Z14ejpnt.js 1.82 kB +1.82 kB (new file) 🆕
packages/frontend/dist/assets/StatTile-CyLfkHvp.js 0 B -634 B (removed) 🏆
packages/frontend/dist/assets/StatTile-Dp0KbTBX.js 639 B +639 B (new file) 🆕
packages/frontend/dist/assets/Support-5qkN_PLE.js 1.55 kB +1.55 kB (new file) 🆕
packages/frontend/dist/assets/Support-yr0pyWDi.js 0 B -1.55 kB (removed) 🏆
packages/frontend/dist/assets/switch-B6Du6Tja.js 0 B -541 B (removed) 🏆
packages/frontend/dist/assets/switch-Bh7G1mdE.js 543 B +543 B (new file) 🆕
packages/frontend/dist/assets/TermsOfService-BYh8IZok.js 1.59 kB +1.59 kB (new file) 🆕
packages/frontend/dist/assets/TermsOfService-D-YXox5W.js 0 B -1.6 kB (removed) 🏆
packages/frontend/dist/assets/TrackHistory-DRL5PZME.js 0 B -2.31 kB (removed) 🏆
packages/frontend/dist/assets/TrackHistory-g8_g-A1C.js 2.31 kB +2.31 kB (new file) 🆕
packages/frontend/dist/assets/TwitchNotifications-BFNZqgxA.js 2.44 kB +2.44 kB (new file) 🆕
packages/frontend/dist/assets/TwitchNotifications-Db3NT1Yr.js 0 B -2.44 kB (removed) 🏆
packages/frontend/dist/assets/useActiveHeading-4aCIW3Od.js 0 B -1.35 kB (removed) 🏆
packages/frontend/dist/assets/useActiveHeading-C-TsO6ps.js 1.35 kB +1.35 kB (new file) 🆕
packages/frontend/dist/assets/useFeatures-BVkQW1wR.js 2.07 kB +2.07 kB (new file) 🆕
packages/frontend/dist/assets/useFeatures-D3wDCZsL.js 0 B -2.06 kB (removed) 🏆
packages/frontend/dist/assets/vendor-ui-BC7l0ZUw.js 65.9 kB +65.9 kB (new file) 🆕
packages/frontend/dist/assets/vendor-ui-Dy3hnobj.js 0 B -65.8 kB (removed) 🏆
ℹ️ View Unchanged
Filename Size
packages/frontend/dist/assets/index-wcDJcD1b.css 18.1 kB
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-qkfmDH9H.js 39.9 kB
packages/frontend/dist/assets/vendor-react-B7C34xnu.js 55.7 kB
packages/frontend/dist/assets/vendor-state-Dw4-MN6C.js 24.2 kB

compressed-size-action

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: 9

🧹 Nitpick comments (1)
packages/backend/tests/unit/services/RoleGroupService.test.ts (1)

178-215: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Tie-break test should include non-Primary-first ordering.

The current tie test still passes if implementation chooses first-seen style. Add a variant where 'Secondary' is seen before 'Primary' and still expect 'Primary'.

Proposed test adjustment
             const mockMappings = [
-                { id: 'map-1', roleId: ROLE_ID_A, style: 'Primary' },
-                { id: 'map-2', roleId: ROLE_ID_B, style: 'Secondary' },
+                { id: 'map-1', roleId: ROLE_ID_A, style: 'Secondary' },
+                { id: 'map-2', roleId: ROLE_ID_B, style: 'Primary' },
             ]
🤖 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/tests/unit/services/RoleGroupService.test.ts` around lines
178 - 215, The tie-break test in the 'defaults buttonStyle to Primary on tie'
test case needs to verify that Primary is chosen even when encountered in a
non-first position. Reorder the mockMappings array so that the Secondary style
mapping appears before the Primary style mapping, while keeping the expectation
that result.buttonStyle should still be 'Primary'. This ensures the test
validates the actual tie-break logic that defaults to Primary regardless of
which style is seen first.
🤖 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/roleGroups.ts`:
- Around line 124-138: The guildId parameter is extracted from the request but
not passed to the roleGroupService.addRoleToGroup method call, which creates a
security vulnerability allowing cross-guild mutations if a foreign group ID is
supplied. Pass guildId as a parameter to the addRoleToGroup method call
alongside id, data, and botToken, and ensure the service implementation
validates that the group being modified actually belongs to the specified guild
before performing the mutation.

In `@packages/backend/src/schemas/management.ts`:
- Around line 165-168: The fromMessageId field validation in the management
schema is using a regex pattern that matches Discord snowflakes (17-20 digits),
but the RoleGroupService.createRoleGroup method actually queries
reactionRoleMessage by its internal id field, not the Discord snowflake format.
Update the regex validation for the fromMessageId field to match the actual
internal ID format used in the reactionRoleMessage table instead of the Discord
snowflake pattern to prevent valid requests from being rejected during
validation.

In `@packages/backend/src/services/RoleGroupService.ts`:
- Around line 166-176: The message lookup in RoleGroupService.ts when handling
req.fromMessageId is missing a guild ownership validation. After the findUnique
query retrieves the message record, add a check to verify that message.guildId
matches req.guildId before proceeding. If the guild IDs do not match, throw an
appropriate authorization error (such as AppError.forbidden). This same
validation pattern should be applied to all similar message lookups in the
service, including the referenced lines 199-203.
- Around line 31-35: Replace all instances of the `any` type in the
RoleGroupService.ts file with specific, properly typed definitions. In the
AddRoleToGroupResult interface, replace the `any` type for the `mapping` field
with an appropriate type definition. Additionally, locate and update the
`createRoleGroup` method return type and the `addResult` variable type (at lines
163, 217-222, and 327) to use concrete types instead of `any`. Determine the
correct types by examining what data structures are actually being returned or
assigned in each case, and define interfaces or types that accurately represent
the shape of the data being used.
- Around line 166-204: The createRoleGroup method has a race condition where the
1:1 constraint check, group creation, and message link update are separate
database operations that can be interleaved by concurrent requests. Wrap all
three operations (the message existence check, the roleGroup.create call, and
the reactionRoleMessage.update call) in a single Prisma transaction to ensure
atomicity and prevent concurrent requests from creating orphan groups. Make the
reactionRoleMessage.update conditional inside the transaction so it only
executes when req.fromMessageId is provided.
- Around line 123-136: The mode selection logic in the styleCount iteration does
not enforce the documented tie-breaking rule that Primary should be selected
when multiple styles have equal counts. The condition on line 132 uses `count >
modeCount` which only updates when a strictly greater count is found, causing
ties to keep the first-seen style instead of preferring Primary. Change the
comparison condition to also update when count equals modeCount and the current
style is Primary, ensuring that Primary wins all tie scenarios and produces the
correctly seeded buttonStyle.

In `@packages/backend/tests/integration/routes/roleGroups.test.ts`:
- Around line 63-65: The DISCORD_TOKEN environment variable is being set at
module scope (outside of test hooks), which causes it to persist across all
tests in the file and can leak to other test suites. Move the
process.env.DISCORD_TOKEN assignment from the module-level scope into a
beforeEach hook so the environment variable is set before each individual test
runs. Optionally add an afterEach hook to restore or delete the DISCORD_TOKEN to
ensure clean state between tests and prevent cross-test pollution.

In `@packages/frontend/src/pages/ReactionRoles.tsx`:
- Around line 892-908: The handleCreateRoleGroup function does not clear the
deleteError state before attempting to create a new role group, which means old
error messages from failed attempts will remain visible even after a subsequent
successful creation. Add a call to setDeleteError with an empty or null value at
the beginning of the handleCreateRoleGroup function, right after the
selectedGuild check and before the try block, to ensure any previous error state
is cleared when a new create attempt begins.

In `@packages/shared/src/services/ReactionRolesService/index.ts`:
- Around line 713-799: The read-validate-insert sequence in this function is not
atomic per message, allowing race conditions where two concurrent requests both
read the message at 24 mappings, both pass the capacity guard, and both insert.
Move the prisma.reactionRoleMessage.findUnique call and all validation checks
(capacity check and duplicate roleId check) inside the prisma.$transaction block
so that the entire read-validate-insert operation is serialized as a single
atomic transaction per message, ensuring concurrent requests cannot both bypass
the 25-mapping capacity guard.

---

Nitpick comments:
In `@packages/backend/tests/unit/services/RoleGroupService.test.ts`:
- Around line 178-215: The tie-break test in the 'defaults buttonStyle to
Primary on tie' test case needs to verify that Primary is chosen even when
encountered in a non-first position. Reorder the mockMappings array so that the
Secondary style mapping appears before the Primary style mapping, while keeping
the expectation that result.buttonStyle should still be 'Primary'. This ensures
the test validates the actual tie-break logic that defaults to Primary
regardless of which style is seen first.
🪄 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: 2d9eea67-fa48-47d7-92b9-7c856ee983ca

📥 Commits

Reviewing files that changed from the base of the PR and between fda6eea and 2fbcc21.

📒 Files selected for processing (21)
  • decisions/2026-06-23-role-groups-composite-actions.md
  • packages/backend/src/errors/AppError.ts
  • packages/backend/src/routes/index.ts
  • packages/backend/src/routes/roleGroups.ts
  • packages/backend/src/schemas/management.ts
  • packages/backend/src/services/RoleGroupService.ts
  • packages/backend/tests/integration/routes/roleGroups.test.ts
  • packages/backend/tests/unit/routes/index.test.ts
  • packages/backend/tests/unit/services/RoleGroupService.test.ts
  • packages/frontend/src/components/reactionRoles/AddStyledRoleForm.test.tsx
  • packages/frontend/src/components/reactionRoles/AddStyledRoleForm.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.ts
  • packages/frontend/src/services/roleGroupsApi.test.ts
  • packages/frontend/src/services/roleGroupsApi.ts
  • packages/shared/src/services/ReactionRolesService/index.spec.ts
  • packages/shared/src/services/ReactionRolesService/index.ts
  • prisma/migrations/20260623120000_add_role_groups/migration.sql
  • prisma/schema.prisma
📜 Review details
⏰ Context from checks skipped due to timeout. (13)
  • GitHub Check: Test — bot
  • GitHub Check: Test — backend
  • GitHub Check: Test — shared
  • GitHub Check: Test — frontend
  • GitHub Check: cubic · AI code reviewer
  • GitHub Check: quality / Lint (lint)
  • GitHub Check: quality / SAST (CodeQL) (javascript-typescript)
  • GitHub Check: Build — backend
  • GitHub Check: Build — frontend
  • GitHub Check: Build — bot
  • GitHub Check: compressed-size
  • GitHub Check: Mutation — shared
  • GitHub Check: Mutation — backend
🧰 Additional context used
🪛 ast-grep (0.44.0)
packages/backend/tests/integration/routes/roleGroups.test.ts

[warning] 76-76: Express application should use Helmet
Context: express()
Note: [CWE-693] Protection Mechanism Failure (Express app without Helmet security headers).

(missing-helmet-typescript)

🪛 GitHub Check: Checks
packages/backend/src/routes/roleGroups.ts

[failure] 124-124:
'guildId' is assigned a value but never used. Allowed unused vars must match /^_/u


[failure] 96-96:
Unsafe assignment of an any value


[failure] 77-77:
Unsafe assignment of an any value


[failure] 27-27:
Unsafe assignment of an any value

packages/backend/src/services/RoleGroupService.ts

[failure] 367-367:
Unsafe assignment of an any value


[failure] 360-360:
Unsafe member access .status on an any value


[failure] 327-327:
Unexpected any. Specify a different type


[failure] 221-221:
Unexpected any. Specify a different type


[warning] 213-213:
Async method 'addRoleToGroup' has a complexity of 16. Maximum allowed is 15


[failure] 163-163:
Unexpected any. Specify a different type


[warning] 66-66:
Async method 'seedStyleFromMessage' has a complexity of 21. Maximum allowed is 15


[failure] 34-34:
Unexpected any. Specify a different type

🔇 Additional comments (20)
prisma/migrations/20260623120000_add_role_groups/migration.sql (1)

1-37: LGTM!

prisma/schema.prisma (1)

98-98: LGTM!

Also applies to: 563-578, 600-618, 1020-1024, 1093-1099

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

1823-2163: LGTM!

packages/frontend/src/services/reactionRolesApi.ts (1)

18-18: LGTM!

packages/frontend/src/services/roleGroupsApi.ts (1)

1-121: LGTM!

packages/frontend/src/services/api.ts (1)

34-34: LGTM!

Also applies to: 434-434

packages/frontend/src/services/roleGroupsApi.test.ts (1)

1-222: LGTM!

packages/frontend/src/components/reactionRoles/AddStyledRoleForm.tsx (1)

1-337: LGTM!

packages/frontend/src/pages/ReactionRoles.tsx (1)

21-21: LGTM!

Also applies to: 117-255, 1013-1017

packages/frontend/src/components/reactionRoles/AddStyledRoleForm.test.tsx (1)

1-492: LGTM!

packages/frontend/src/pages/ReactionRoles.test.tsx (1)

2271-2472: LGTM!

packages/backend/src/errors/AppError.ts (1)

47-49: LGTM!

packages/backend/src/routes/index.ts (1)

15-15: LGTM!

Also applies to: 68-72, 89-89

packages/backend/tests/unit/routes/index.test.ts (1)

18-18: LGTM!

Also applies to: 96-99, 233-237, 250-250

decisions/2026-06-23-role-groups-composite-actions.md (6)

34-47: DB-first + compensation pattern is well-documented and coherent.

The design clearly articulates the v1 failure-mode trade-off: DB-first append inverts the orphaned-button risk (Discord-first) to stale-visuals (DB-first, which re-sync), and compensation (delete role on mapping failure) addresses the asymmetric risk. The code snippet confirms this pattern is implemented (RoleGroupService.ts lines 303–323). The lightweight idempotency deferred to v2 is a reasonable scope boundary, given that bulk/high-concurrency operations are out of v1 scope.


28-28: 1:1 uniqueness constraint properly enforced in v1.

The design correctly specifies that v1 uses a 1:1 mapping (one RoleGroup per ReactionRoleMessage), enforced by the @@unique([groupId]) constraint (verified in Prisma schema snippet). Multi-message groups are appropriately deferred to v2, where the uniqueness constraint would be relaxed. Clear scope boundary.

Also applies to: 58-58


54-54: Color storage and conversion design is sound.

Storing color as a hex String (matching UserPreferences.embedColor) and converting to Int at Discord call time (hexToInt, verified in RoleGroupService.ts line 299) is a good choice for consistency and Discord API compatibility. The design clearly explains the rationale.

Also applies to: 90-90


74-76: 🎯 Functional Correctness

All specified role-groups routes are correctly implemented.

Verification confirms all five endpoints are present in packages/backend/src/routes/roleGroups.ts with correct HTTP methods and path structures:

  • POST /api/guilds/:guildId/role-groups with fromMessageId and style parameters (lines 17–57)
  • GET /api/guilds/:guildId/role-groups to list groups (lines 59–68)
  • PATCH /api/guilds/:guildId/role-groups/:id to update groups (lines 85–116)
  • POST /api/guilds/:guildId/role-groups/:id/roles to add roles with full parameter support (lines 118–172)
  • DELETE /api/guilds/:guildId/role-groups/:id/roles/:roleId to detach roles (lines 174–193)

88-89: 🔒 Security & Privacy

Design assumption about position 0 is correct but relies on Discord API defaults.

New roles are indeed created at position 0 (bottom) as specified in the design, preventing privilege escalation. However, the implementation relies on Discord API's default behavior rather than explicitly enforcing it. The createGuildRole method in GuildService.ts (lines 960–1030) does not pass a position parameter to either guild.roles.create() (discord.js path) or the Discord API endpoint. Discord API defaults to position 0 when position is omitted, so the design goal is achieved, but adding an explicit position: 0 would improve security clarity and robustness against future changes.


64-65: 🎯 Functional Correctness

seedStyleFromMessage correctly implements the design specification.

The implementation in RoleGroupService.seedStyleFromMessage (lines 69–146) correctly computes:

  • Modal color/hoist/mentionable: Counts occurrences across sibling roles and selects the most frequent value for each.
  • Mode buttonStyle: Counts occurrences across ReactionRoleMapping.style entries and selects the most frequent.
  • Tie-breaking to Primary: Initializes modeStyle = 'Primary' with modeCount = 0, so when no style exceeds count 0 (a tie scenario), Primary is preserved.

The unit test "defaults buttonStyle to Primary on tie" (lines 221–243) validates this behavior explicitly. The divergence flag is also correctly set when color, hoist, or mentionable values differ across roles.

Comment thread packages/backend/src/routes/roleGroups.ts
Comment thread packages/backend/src/schemas/management.ts Outdated
Comment thread packages/backend/src/services/RoleGroupService.ts
Comment thread packages/backend/src/services/RoleGroupService.ts
Comment thread packages/backend/src/services/RoleGroupService.ts
Comment thread packages/backend/src/services/RoleGroupService.ts
Comment thread packages/backend/tests/integration/routes/roleGroups.test.ts Outdated
Comment thread packages/frontend/src/pages/ReactionRoles.tsx
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.

6 issues found across 21 files

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/frontend/src/services/roleGroupsApi.ts
Comment thread packages/frontend/src/pages/ReactionRoles.tsx
Comment thread packages/backend/src/schemas/management.ts
Comment thread packages/backend/src/schemas/management.ts
Addresses PR #1557 review (CodeRabbit/CodeQL):
- SECURITY (IDOR): addRoleToGroup now takes guildId and scopes the group
  lookup by (id, guildId); createRoleGroup verifies message.guildId ===
  req.guildId before linking. Prevents cross-guild mutation via a foreign id.
- FIX: createRoleGroupBody.fromMessageId accepted only a Discord snowflake but
  the UI/service use the message cuid id -> every create-group call would 400.
  Now min(1).
- FIX: seedStyleFromMessage now honours the documented tie -> Primary rule
  (was keeping first-seen on a tie).
- createRoleGroup wraps create+link in a transaction with a conditional
  updateMany to close the check/create/link race.
- minor: DISCORD_TOKEN restored in integration tests; stale create-group error
  cleared before retry.
- types: tightened service contracts (boundary any from addRoleToMessage left
  as localized eslint-disable; cleaner typing tracked as follow-up).

New tests: cross-guild rejection (addRoleToGroup + createRoleGroup), real
non-Primary tie. Backend lint 0 errors; backend 1202 + frontend 876 green.
Deferred to v2 (issue #1558): per-message append serialization. Filed #1559:
CI lint gate false-green on type-aware rules.

Ref: decisions/2026-06-23-role-groups-composite-actions.md

@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 7 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread packages/backend/tests/integration/routes/roleGroups.test.ts
Addresses cubic P2 on #1557: the afterAll restore assigned
process.env.DISCORD_TOKEN = originalDiscordToken, which stores the literal
string "undefined" when the token was never set, polluting other suites that
assert its absence. Now deletes the key when it was originally unset.

@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 1 file (changes from recent commits).

Requires human review: Auto-approval blocked by 5 unresolved issues from previous reviews.

Re-trigger cubic

Addresses CodeRabbit CLI review of the branch:
- route imports the exported roleGroupService singleton instead of
  constructing a second instance (integration-test mock updated to provide
  the singleton)
- addRoleToMessage validates the new mapping's role id as a Discord snowflake
  before the DB insert, so an invalid id can't be persisted then orphaned by a
  failing Discord PATCH
- unit tests now mock roleGroup.findFirst (the method the IDOR-hardened code
  actually calls) instead of findUnique — they previously passed by rejecting
  early on "group not found", masking the real preflight assertions
- documented that addRoleToMessage's capacity guard is not DB-constrained
  (concurrency hardening tracked in #1558; duplicate role ids are already
  enforced atomically by the unique index)

Backend 1202 + shared 1286 green; type:check + backend lint clean.

@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 4 files (changes from recent commits).

Requires human review: Auto-approval blocked by 5 unresolved issues from previous reviews.

Re-trigger cubic

Addresses the scoped CodeRabbit CLI re-review (2 findings, both in the new
ReactionRoles.tsx code):
- i18n: the new "Create role group" / "Add styled role" labels, the
  create-group title, the default group name, and the create-error message
  were hardcoded English on a now-internationalised page. Wrapped them in t()
  and added reactionRoles.* keys to en.json + pt-BR.json.
- UX: the "Add styled role" disclosure had no way to close once opened
  (showAddRole only reset on a successful refetch). Added a Cancel button that
  resets it.

Frontend suite 861 + role-group focused 87 green; type:check + lint clean.
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: 2

🤖 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/locales/pt-BR.json`:
- Line 826: In the pt-BR.json file, the loadFailedError key contains a typo
where "carregamentar" should be corrected to "carregar". The word
"carregamentar" does not exist in Portuguese; it appears to be an accidental
combination of the verb "carregar" (to load) with an extra suffix. Replace the
misspelled word in the error message string with the correct verb form
"carregar" to properly convey the meaning of failing to load messages by role
reaction.
- Around line 19-22: In the pt-BR.json locale file, the string values for the
keys "name", "error", "color", and "selectServerDesc" are currently in English
and must be translated to Brazilian Portuguese to ensure a consistent user
interface for Portuguese speakers. Replace the English values for each of these
four keys with their appropriate Portuguese (Brazil) translations.
🪄 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: 68321620-2da2-4a93-9271-256d67987b23

📥 Commits

Reviewing files that changed from the base of the PR and between 98495f5 and 7ae4f83.

📒 Files selected for processing (7)
  • packages/backend/src/routes/roleGroups.ts
  • packages/backend/tests/integration/routes/roleGroups.test.ts
  • packages/backend/tests/unit/services/RoleGroupService.test.ts
  • packages/frontend/src/locales/en.json
  • packages/frontend/src/locales/pt-BR.json
  • packages/frontend/src/pages/ReactionRoles.tsx
  • packages/shared/src/services/ReactionRolesService/index.ts
✅ Files skipped from review due to trivial changes (1)
  • packages/frontend/src/locales/en.json
🚧 Files skipped from review as they are similar to previous changes (5)
  • packages/shared/src/services/ReactionRolesService/index.ts
  • packages/backend/tests/unit/services/RoleGroupService.test.ts
  • packages/backend/tests/integration/routes/roleGroups.test.ts
  • packages/frontend/src/pages/ReactionRoles.tsx
  • packages/backend/src/routes/roleGroups.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (11)
  • GitHub Check: Test — shared
  • GitHub Check: Test — frontend
  • GitHub Check: Test — backend
  • GitHub Check: Checks
  • GitHub Check: Test — bot
  • GitHub Check: cubic · AI code reviewer
  • GitHub Check: quality / SAST (CodeQL) (javascript-typescript)
  • GitHub Check: quality / Lint (lint)
  • GitHub Check: Build — bot
  • GitHub Check: Build — backend
  • GitHub Check: compressed-size

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

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 2

🤖 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/locales/pt-BR.json`:
- Line 826: In the pt-BR.json file, the loadFailedError key contains a typo
where "carregamentar" should be corrected to "carregar". The word
"carregamentar" does not exist in Portuguese; it appears to be an accidental
combination of the verb "carregar" (to load) with an extra suffix. Replace the
misspelled word in the error message string with the correct verb form
"carregar" to properly convey the meaning of failing to load messages by role
reaction.
- Around line 19-22: In the pt-BR.json locale file, the string values for the
keys "name", "error", "color", and "selectServerDesc" are currently in English
and must be translated to Brazilian Portuguese to ensure a consistent user
interface for Portuguese speakers. Replace the English values for each of these
four keys with their appropriate Portuguese (Brazil) translations.
🪄 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: 68321620-2da2-4a93-9271-256d67987b23

📥 Commits

Reviewing files that changed from the base of the PR and between 98495f5 and 7ae4f83.

📒 Files selected for processing (7)
  • packages/backend/src/routes/roleGroups.ts
  • packages/backend/tests/integration/routes/roleGroups.test.ts
  • packages/backend/tests/unit/services/RoleGroupService.test.ts
  • packages/frontend/src/locales/en.json
  • packages/frontend/src/locales/pt-BR.json
  • packages/frontend/src/pages/ReactionRoles.tsx
  • packages/shared/src/services/ReactionRolesService/index.ts
✅ Files skipped from review due to trivial changes (1)
  • packages/frontend/src/locales/en.json
🚧 Files skipped from review as they are similar to previous changes (5)
  • packages/shared/src/services/ReactionRolesService/index.ts
  • packages/backend/tests/unit/services/RoleGroupService.test.ts
  • packages/backend/tests/integration/routes/roleGroups.test.ts
  • packages/frontend/src/pages/ReactionRoles.tsx
  • packages/backend/src/routes/roleGroups.ts
🛑 Comments failed to post (2)
packages/frontend/src/locales/pt-BR.json (2)

19-22: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Translate remaining English strings in pt-BR locale.

These four values are still in English, which causes mixed-language UI for Brazilian Portuguese users.

💬 Suggested translation patch
-        "name": "Name",
-        "error": "Error",
-        "color": "Color",
-        "selectServerDesc": "Select a server to continue"
+        "name": "Nome",
+        "error": "Erro",
+        "color": "Cor",
+        "selectServerDesc": "Selecione um servidor para continuar"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

        "name": "Nome",
        "error": "Erro",
        "color": "Cor",
        "selectServerDesc": "Selecione um servidor para continuar"
🤖 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/locales/pt-BR.json` around lines 19 - 22, In the
pt-BR.json locale file, the string values for the keys "name", "error", "color",
and "selectServerDesc" are currently in English and must be translated to
Brazilian Portuguese to ensure a consistent user interface for Portuguese
speakers. Replace the English values for each of these four keys with their
appropriate Portuguese (Brazil) translations.

826-826: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fix typo in localized error message.

carregamentar appears to be a typo.

✏️ Suggested fix
-        "loadFailedError": "Falha ao carregamentar mensagens de cargo por reação.",
+        "loadFailedError": "Falha ao carregar mensagens de cargo por reação.",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

        "loadFailedError": "Falha ao carregar mensagens de cargo por reação.",
🤖 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/locales/pt-BR.json` at line 826, In the pt-BR.json
file, the loadFailedError key contains a typo where "carregamentar" should be
corrected to "carregar". The word "carregamentar" does not exist in Portuguese;
it appears to be an accidental combination of the verb "carregar" (to load) with
an extra suffix. Replace the misspelled word in the error message string with
the correct verb form "carregar" to properly convey the meaning of failing to
load messages by role reaction.

@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 3 files (changes from recent commits).

Requires human review: Auto-approval blocked by 5 unresolved issues from previous reviews.

Re-trigger cubic

RoleGroupService imports reactionRolesService from
'@lucky/shared/services/ReactionRolesService', but that directory barrel had
no explicit exports entry — the ./services/* wildcard maps only flat files,
not directories. At runtime (Node ESM resolution of the built package) the
backend would ERR_MODULE_NOT_FOUND loading RoleGroupService; type:check and
jest masked it (tsconfig paths / moduleNameMapper). Added an explicit
./services/ReactionRolesService entry, mirroring ./services/guildAutomation.

Caught by the verify:shared-exports CI gate. verify:shared-exports now passes.
@github-actions github-actions Bot added the dependencies Pull requests that update a dependency file label Jun 23, 2026

@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 1 file (changes from recent commits).

Requires human review: Auto-approval blocked by 5 unresolved issues from previous reviews.

Re-trigger cubic

@sonarqubecloud

Copy link
Copy Markdown

@LucasSantana-Dev
LucasSantana-Dev dismissed stale reviews from coderabbitai[bot] and coderabbitai[bot] June 23, 2026 23:41

All findings addressed: 2 IDORs fixed, cuid validation, type-aware lint, i18n, and a runtime shared-exports bug; scoped CodeRabbit re-review returned 0 findings. CI 43/43 green.

@LucasSantana-Dev
LucasSantana-Dev merged commit c869811 into main Jun 23, 2026
49 checks passed
@LucasSantana-Dev
LucasSantana-Dev deleted the feat/role-groups branch June 23, 2026 23:43
LucasSantana-Dev added a commit that referenced this pull request Jun 24, 2026
🤖 I have created a release *beep* *boop*
---


<details><summary>2.23.0</summary>

##
[2.23.0](v2.22.0...v2.23.0)
(2026-06-24)


### Features

* **role-groups:** composite add-styled-role v1
([#1557](#1557))
([c869811](c869811))
</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.23.0 adds the first version of the role-groups composite
“add-styled-role” and updates version metadata and the changelog.

- **New Features**
  - Role Groups: introduce composite "add-styled-role" v1

<sup>Written for commit 81904af.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/LucasSantana-Dev/Lucky/pull/1565?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 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 — abdb6c12 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

Labels

backend database dependencies Pull requests that update a dependency file enhancement New feature or request frontend shared size/xl

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants