discourse/spec
Régis Hanol 5cef23ef63
FEATURE: award badges based on topic votes received (#39493)
Adds four tiered badges to discourse-topic-voting so topic authors are
recognized when their ideas get traction:

- Daydreamer (Bronze, 1 vote)
- Brainstormer (Silver, 5 votes)
- Innovator (Silver, 15 votes)
- Visionary (Gold, 25 votes)

Badges are multi-grant and tied to the qualifying topic's first post, so
a user earns each tier once per topic that reaches the threshold.
Self-votes (voting on your own topic) are excluded. All badges are
disabled by default; the Silver and Gold tiers (Brainstormer, Innovator,
Visionary) allow the badge to be used as a title when enabled.

<img width="287" height="216" alt="image"
src="https://github.com/user-attachments/assets/3a84c2b2-6157-4504-b5a5-45e7c660e90c"
/>


### How it is wired

- `Votes::Cast` enqueues a `BackfillBadges` job after a vote is cast.
- `TopicMerger.merge` and `VoteReclaim` also enqueue the job so merged
or reclaimed topics are re-evaluated immediately rather than having to
wait for the daily consistency pass.
- The job calls `BadgeGranter.backfill` scoped to the topic's first
post. Queries join `badge_posts`, which already filters out deleted and
unlisted topics, read-restricted categories, and categories with
`allow_badges` disabled.
- Each query returns `granted_at` as the timestamp of the Nth qualifying
vote (via `ROW_NUMBER()`), so every tier reflects when that threshold
was actually crossed rather than when the most recent vote landed.
- Revocation (vote removed, topic deleted, category changed) runs on the
daily full backfill via `auto_revoke`, consistent with how
discourse-solved handles the same pattern.

### Notification handling

To avoid "granted badge" notification avalanches when the feature is
first enabled on a community with years of existing votes, core now
exposes a `:badge_granter_suppress_notification` modifier. The plugin
registers it and suppresses notifications for its four badges when the
qualifying vote is older than 2 weeks. Combined with the per-tier
`granted_at`, this means only the tier the author just crossed produces
a notification; lower tiers whose threshold was reached long ago stay
silent.

Ref - t/182304
2026-04-28 10:53:06 +10:00
..
db DEV: Silence expected error/debug output in core specs (#39247) 2026-04-14 11:04:10 +02:00
fabricators FEATURE: Add problem checks page to admin panel and allow ignoring problem checks (#39103) 2026-04-23 08:28:33 +08:00
fixtures FIX: Better handling of github_folder onebox w/o opengraph tags (#39142) 2026-04-08 11:49:57 +02:00
generator DEV: Remove deprecated trust level access settings (#36414) 2025-12-11 11:09:29 +10:00
helpers FIX: Make twitter card meta data translatable (#39150) 2026-04-08 18:40:21 +02:00
import_export DEV: add shortcut fab!(:variable, :fabricator) to specs (#33577) 2025-07-11 11:16:34 -03:00
initializers FEATURE: Notify admins of upcoming changes and log events (#37003) 2026-01-21 12:45:54 +10:00
integration FEATURE: add modifier to restrict theme color schemes (#38796) 2026-03-26 09:03:11 -04:00
integrity FEATURE: Allow upcoming changes to control site setting defaults (#38815) 2026-04-08 13:05:44 +10:00
jobs FEATURE: better email subject lines (#36040) 2026-04-24 15:14:10 +04:00
lib DEV: Don't forward cookies on onebox redirects (#39544) 2026-04-27 12:36:29 +02:00
mailers FEATURE: better email subject lines (#36040) 2026-04-24 15:14:10 +04:00
models FEATURE: better email subject lines (#36040) 2026-04-24 15:14:10 +04:00
multisite DEV: Fix spec cleanup issues for upcoming changes (#39024) 2026-04-01 14:15:02 +10:00
queries/reports UX: Add more reports to legacy, removing them from the reports page (#38688) 2026-03-18 22:18:47 +08:00
requests DEV: Fix a flake in post_controller spec (#39554) 2026-04-27 12:37:02 +02:00
script/import_scripts
serializers FIX: Persist group default tag notification settings (#39369) 2026-04-27 11:40:06 +02:00
services FEATURE: award badges based on topic votes received (#39493) 2026-04-28 10:53:06 +10:00
support DEV: Fix flaky select-kit test selectors (#39577) 2026-04-27 12:42:10 +02:00
system DEV: Restore and fix the full-page search spec (#39586) 2026-04-27 23:27:33 +02:00
tasks DEV: Silence expected error/debug output in core specs (#39247) 2026-04-14 11:04:10 +02:00
views FIX: remove ItemList schema from linkbacks in crawler view (#36608) 2025-12-11 10:41:47 +01:00
rails_helper.rb DEV: Restore and fix composer body test (#39510) 2026-04-27 00:18:31 +02:00
regenerate_swagger_docs
swagger_helper.rb DEV: add api docs for discourse-calendar events index endpoint (#35400) 2025-10-16 07:39:08 +08:00