Context
SlackWeeklyLeaderboardTask.run() iterates workspaceRepository.findAll() and, for each workspace with qualified reviewers, posts to the global hephaestus.leaderboard.notification.channel-id using the global Slack app token. With N active workspaces this produces N Slack messages on the same channel at every leaderboard cycle end.
Observed in prod on channel G6TCVL6HL:
- Week of 2026-04-14 09:00 CEST: 2 messages (workspaces
ls1intum, ios26-intro-course).
- Week of 2026-04-21 09:00 CEST: 3 messages (
ls1intum, ios26-intro-course, prompt-edu). Log confirms Skipped Slack notification: reason=noQualifiedReviewers, workspaceId=2 for the purged fcorporate workspace — other workspaces were not filtered.
The DB schema already has per-workspace Slack fields (slack_token, slack_signing_secret, leaderboard_notification_enabled, leaderboard_notification_channel_id, leaderboard_notification_team, leaderboard_schedule_day, leaderboard_schedule_time) added by changelog 1762784082609, but SlackWeeklyLeaderboardTask and SlackMessageService still use the global config. The migration is half-finished.
Stopgap applied
Added hephaestus.leaderboard.notification.workspace-slugs (env LEADERBOARD_NOTIFICATION_WORKSPACE_SLUGS). Only listed slugs receive notifications. Validation requires the list to be non-empty when notifications are enabled. To be removed alongside the follow-up work below.
Follow-up
Finish migrating notifications to per-workspace routing:
- Honor each workspace's
leaderboardNotificationEnabled, leaderboardNotificationChannelId, leaderboardNotificationTeam, and leaderboardScheduleDay/leaderboardScheduleTime instead of the global hephaestus.leaderboard.* config.
- Send via a per-workspace Slack client built from
slackToken / slackSigningSecret, not the single global Slack app.
- Skip non-
ACTIVE workspaces explicitly.
- Provide an admin UI / API path to configure per-workspace Slack settings.
- Remove
hephaestus.leaderboard.notification.{channel-id,team,workspace-slugs} and the shared SlackMessageService/global Slack app once every live workspace has migrated.
PR #771 (fix/leaderboard-workspace-notifications) already starts this direction but needs rework given the current schema.
Related
Context
SlackWeeklyLeaderboardTask.run()iteratesworkspaceRepository.findAll()and, for each workspace with qualified reviewers, posts to the globalhephaestus.leaderboard.notification.channel-idusing the global Slack app token. With N active workspaces this produces N Slack messages on the same channel at every leaderboard cycle end.Observed in prod on channel
G6TCVL6HL:ls1intum,ios26-intro-course).ls1intum,ios26-intro-course,prompt-edu). Log confirmsSkipped Slack notification: reason=noQualifiedReviewers, workspaceId=2for the purgedfcorporateworkspace — other workspaces were not filtered.The DB schema already has per-workspace Slack fields (
slack_token,slack_signing_secret,leaderboard_notification_enabled,leaderboard_notification_channel_id,leaderboard_notification_team,leaderboard_schedule_day,leaderboard_schedule_time) added by changelog1762784082609, butSlackWeeklyLeaderboardTaskandSlackMessageServicestill use the global config. The migration is half-finished.Stopgap applied
Added
hephaestus.leaderboard.notification.workspace-slugs(envLEADERBOARD_NOTIFICATION_WORKSPACE_SLUGS). Only listed slugs receive notifications. Validation requires the list to be non-empty when notifications are enabled. To be removed alongside the follow-up work below.Follow-up
Finish migrating notifications to per-workspace routing:
leaderboardNotificationEnabled,leaderboardNotificationChannelId,leaderboardNotificationTeam, andleaderboardScheduleDay/leaderboardScheduleTimeinstead of the globalhephaestus.leaderboard.*config.slackToken/slackSigningSecret, not the single global Slack app.ACTIVEworkspaces explicitly.hephaestus.leaderboard.notification.{channel-id,team,workspace-slugs}and the sharedSlackMessageService/global Slack app once every live workspace has migrated.PR #771 (
fix/leaderboard-workspace-notifications) already starts this direction but needs rework given the current schema.Related