feat(kudos): /kudos recognition command + leaderboard (LC-526) #504

Merged
longjacksonle merged 4 commits from feat/LC-526-kudos into main 2026-07-03 03:59:31 +02:00

Adds team recognition: a /kudos @user <reason> slash command and a leaderboard, the culture feature from the round-2 brainstorm shortlist.

Giving: /kudos @user <reason> records a kudos and posts a "Kudos to @user: reason" message authored by the giver. The @mention rides the existing finalize_message_send pipeline, so the receiver is notified for free. Self-kudos and unknown users are rejected; the reason is optional and capped at 500 chars.

Leaderboard: GET /kudos (new sidebar nav row) shows two lists - Most appreciated (top receivers) and Most generous (top givers) - over the past 30 days, scoped to the enclaves the viewer belongs to so cross-enclave tallies never leak. It is a plain aggregate over the new kudos table (migration 0081), reusing the pins/highlights page shape.

Shipped as four small logical commits: db layer, slash command, leaderboard page + nav, tests.

Moderation posture (per the brainstorm's call-outs): additive only (no downvotes, no remove path), enclave-scoped so only teammates see a board, and the recognition messages are normal room posts that mods already see and can delete. Deferred: a per-user opt-out of the public board (noted for follow-up; the enclave scoping + positive-only design covers the main concern).

Tests: db-layer ranking / enclave-scoping / window / limit (db_kudos.rs); i18n parity (en + es). just test + just test-saas + just check green. The /kudos HTTP path is not integration-tested (SSO-gated harness); its parsing is trivial and the aggregation it feeds is covered.

Not operator-visible (no env/config/security change).

🤖 Generated with Claude Code

Adds team recognition: a `/kudos @user <reason>` slash command and a leaderboard, the culture feature from the round-2 brainstorm shortlist. **Giving:** `/kudos @user <reason>` records a kudos and posts a "Kudos to @user: reason" message authored by the giver. The `@mention` rides the existing `finalize_message_send` pipeline, so the receiver is notified for free. Self-kudos and unknown users are rejected; the reason is optional and capped at 500 chars. **Leaderboard:** `GET /kudos` (new sidebar nav row) shows two lists - Most appreciated (top receivers) and Most generous (top givers) - over the past 30 days, scoped to the enclaves the viewer belongs to so cross-enclave tallies never leak. It is a plain aggregate over the new `kudos` table (migration 0081), reusing the pins/highlights page shape. Shipped as four small logical commits: db layer, slash command, leaderboard page + nav, tests. **Moderation posture** (per the brainstorm's call-outs): additive only (no downvotes, no remove path), enclave-scoped so only teammates see a board, and the recognition messages are normal room posts that mods already see and can delete. Deferred: a per-user opt-out of the public board (noted for follow-up; the enclave scoping + positive-only design covers the main concern). Tests: db-layer ranking / enclave-scoping / window / limit (db_kudos.rs); i18n parity (en + es). `just test` + `just test-saas` + `just check` green. The `/kudos` HTTP path is not integration-tested (SSO-gated harness); its parsing is trivial and the aggregation it feeds is covered. Not operator-visible (no env/config/security change). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Adds the `kudos` table (migration 0081) recording giver/receiver/room/enclave/reason/message, and db::kudos with `record` plus per-enclave leaderboard aggregates (`top_receivers` / `top_givers`, scoped to a set of enclave ids over a datetime window). Additive only - no remove/downvote path. Not yet wired to a command or page.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTRhc4n7A2uB2Y5PHV2ewL
Registers the /kudos built-in and dispatches it: parses `@user <reason>`, rejects self-kudos and unknown users, records the kudos, and posts a "Kudos to @user: reason" message authored by the giver. The @mention rides the existing finalize_message_send pipeline, so the receiver is notified for free. The reason is optional and capped at 500 chars to bound the rendered body (LC-153 spirit). The recognition prefix is localized (en + es).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTRhc4n7A2uB2Y5PHV2ewL
Adds GET /kudos: a two-column leaderboard (most appreciated / most generous) over the past 30 days, scoped to the enclaves the viewer belongs to so cross-enclave tallies never leak. Reached from a new Kudos row in the sidebar navigation. Page + view mirror the pins/highlights shape; i18n en + es.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTRhc4n7A2uB2Y5PHV2ewL
test(kudos): cover leaderboard ranking, enclave scoping, window (LC-526)
All checks were successful
check-secrets / Nosey parker (push) Successful in 3s
check-secrets / TruffleHog (push) Successful in 4s
check-secrets / Kingfisher (push) Successful in 5s
check-secrets / Nosey parker (pull_request) Successful in 4s
check-secrets / TruffleHog (pull_request) Successful in 5s
check-secrets / Kingfisher (pull_request) Successful in 6s
Check / clippy + fmt + tests (pull_request) Successful in 3m54s
Create release / Create release from merged PR (pull_request) Has been skipped
566f1aa90d
db-layer test: records kudos across two enclaves and asserts top_receivers / top_givers rank by count, stay scoped to the queried enclave ids (no cross-enclave leak), exclude out-of-window rows, honor the limit, and return empty for an empty enclave set.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTRhc4n7A2uB2Y5PHV2ewL
longjacksonle scheduled this pull request to auto merge when all checks succeed 2026-07-03 03:57:16 +02:00
longjacksonle deleted branch feat/LC-526-kudos 2026-07-03 03:59:32 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/lets-chat!504
No description provided.