feature: events (LAN mini-seasons)#337
Open
Flegma wants to merge 2 commits into
Open
Conversation
2 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements the approved Events feature: an event is a curated container grouping assigned tournaments; leaderboards and standings are computed on read over the derived match set. Design doc: docs/plans/2026-07-03-events-feature-design.md (workspace docs), implementation plan: docs/plans/2026-07-04-events-feature-implementation-plan.md.
What's included
1867000000300_events: tablesevents,event_organizers,event_tournaments,event_teams,event_players, enume_event_status(Setup/Live/Finished). Event deletion cascades membership rows only; matches/tournaments/players are never touched.is_event_organizercomputed-field function (clone ofis_tournament_organizer).v_event_player_statsview: clone ofv_tournament_player_statskeyed by event, membership derived viaevent_tournaments -> tournament_stages -> tournament_brackets.get_event_leaderboard(_event_id, _category, _match_type, _min_rounds default 10)returningleaderboard_entries; categories rating/adr/kdr/kills/wins; non-emptyevent_playersacts as a roster filter (empty = everyone). Low_min_roundsdefault because the global 50-round floor would blank a one-day LAN.is_organizercomputed field,player_statsmanual relationship, settings-gated insert viapublic.create_events_role(same_existsmechanism as tournaments, full role fan-out), guest select hides Setup events (including all child tables, the stats view, and the leaderboard function), owner-only delete and owner-only co-organizer management, function tracking.Post-review fixes, round 1 (2026-07-04)
An 8-dimension find + adversarial-verify review produced:
status _neq Setupguest gate plus an organizer-awareuserbranch;get_event_leaderboardreturns empty for a Setup or unknown event. Commit5b7a3bf.down.sqlclears the boot-phase SQL digests (guarded byto_regclass) so a forward deploy after a rollback recreates the view/functions. Commitcd8299a.Post-review fixes, round 2 (2026-07-07)
A second full-PR review (8 finder angles, per-candidate adversarial verify, then an adversarial regression pass on the fixes) produced:
get_event_leaderboardhard-capped output atLIMIT 100while the web paginates via Hasura-levelorder_by/limit/offset(like the globalget_leaderboard, which has no cap), silently truncating events with more than 100 players; the cap is removed. An explicit NULL_min_roundsalso silently emptied the board (HAVING >= NULL); it now means "no minimum". Commit1f006a0.create_events_role='moderator'no longer bricks event creation. The insert-check role fan-out skipped the moderator tier entirely (no moderator permission, and the higher roles'_inlists jumped from streamer to match_organizer), so setting the role to moderator would have denied creation to everyone below administrator. Fixed with a moderator permission block and corrected lists. The tournaments clone source (create_tournaments_rolechecks) has the same pre-existing gap; flagged as a follow-up rather than widened into this PR. Commiteed53d6.Verification
Run against an ephemeral TimescaleDB + Hasura v2.49.2 stack with the repo's full migration chain, SQL dirs, metadata, and dev fixtures applied (
metadata ic listconsistent):v_event_player_statsverified row-identical tov_tournament_player_statsfor a single-tournament event (EXCEPT check, both directions), and union growth verified with a second tournament._min_roundsthreshold and_match_typefilter behavior.organizer_steam_id; co-organizers can manage membership but cannot delete the event or manage the co-organizer list (two escalations found and fixed in c77cf4b).Pre-merge checklist (needs the real dev stack)
yarn devboot soHasuraService.setup()hash-applies the new/edited SQL files, thenhasura metadata apply+hasura metadata ic listto confirm no inconsistencies.event_tournaments/v_event_player_stats/get_event_leaderboardreturns zero rows; the organizer still sees them.Product notes for review
get_event_leaderboardtakes no session argument, so it returns empty for a Setup event even to its organizer. This affects the web detail page's Leaderboard tab AND its Teams and Players tab (whose participant fallback uses the same function when no players are explicitly attached); explicitly attached players/teams still show. Raw per-player stats remain visible to the organizer viav_event_player_stats. If organizer preview during Setup is wanted, make the function session-aware (addhasura_session json+is_event_organizercheck) as a follow-up.tournament_organizerrole can manage/delete any event (admin-tier semantics, same asis_event_organizer's role bypass).events.organizer_steam_idFK has no ON DELETE action, so deleting a player who organizes an event is blocked (deliberate; no player-deletion flow exists today).v_event_player_statsderives deaths fromplayer_kills(like its tournament clone) whileget_event_leaderboardusesplayer_match_map_stats, so KDR can differ slightly between the two read paths; the event match-set CTE is duplicated between the view and the function (kept separate to preserve the verified NOT MATERIALIZED inlining); the wins CTE is computed for all categories; no ORDER BY tiebreaker at page boundaries (same as the global leaderboard).Web PR follows (page family + zeus codegen); merge this first.