Proto commits in woogles-io/liwords

These commits are when the Protocol Buffers files have changed: (only the last 100 relevant commits are shown)

Commit:7b540f2
Author:César Del Solar

Bake badge metadata into the frontend bundle GetBadgesMetadata was 33% of all API traffic — ~23k calls/hour, roughly 30M/month. At CloudFront's per-request pricing that is about $31/mo to serve ~500 bytes of data that has changed a handful of times since the badges table was seeded in Feb 2025. The endpoint itself was not the problem. DisplayUserBadges calls useQuery(getBadgesMetadata) once per rendered username, before the badgeCodes guard, so even users with no badges fire a request. The QueryClient is constructed with no defaultOptions, which means staleTime is 0 and refetchOnMount is on, so every observer that mounts refetches. UsernameWithContext renders in 20+ places — every chat message, presence entry, lobby row and player card — so a single lobby page mounts 200-400 observers, and the multiplier is connected clients rather than page loads: one person joining the lobby costs one request on every client. Badge images are already compiled in (badge.tsx dynamically imports ../assets/badges/<code>.png), so adding a badge already required a frontend deploy. Shipping the descriptions the same way costs nothing extra and rides the existing content-hash cache busting — no CloudFront invalidation needed. - add cmd/gen-badges, which reads the badges table via the existing sqlc query and writes liwords-ui/src/gen/badges.ts - read BADGE_DESCRIPTIONS in badge.tsx and profile.tsx - drop the App.tsx prefetch, which was useless anyway since the cached value went stale the instant it landed - add badges.test.ts, asserting every badge code has a matching PNG and vice versa; nothing checked this before - mark GetBriefProfiles and GetBadgesMetadata NO_SIDE_EFFECTS. This is inert until useHttpGet is enabled on the transport, which is a separate change. The admin badge page keeps using the RPC, which remains the source of truth. Also corrects the badgesCacheTTL comment: it credited the hourly sub-badge-updater with changing badge definitions, but that task writes only to user_badges, never to badges. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

The documentation is generated from this commit.

Commit:1afd8a6
Author:César Del Solar

Show how an analysis was run in the analyzer Add an info icon to the analysis summary bar whose hover card reports the engine version, when the analysis ran, which volunteer's worker ran it, how long it took, and how long it waited in the queue. The engine version was already reaching the client as GameAnalysisResult.analyzer_version; it was simply never rendered. The rest lives on analysis_jobs but was not exposed by GetAnalysisResult, so extend GetJobByGameID with claimed_at and a LEFT JOIN for the claiming user's username, and return the derived timings in a new AnalysisRunInfo message. Extending that query rather than adding a second one keeps GetAnalysisResult at a single round trip; the other four callers ignore the new columns. Every timestamp on analysis_jobs is nullable -- jobs predating worker tracking have no claimed_at -- so each derived value is reported as 0 rather than a bogus duration, and the card renders only the rows a job actually recorded. The requester is deliberately not shown. Requesters must be players in the game, so surfacing one would reveal which player asked for the analysis. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

Commit:4f7e3b0
Author:César Del Solar

Show estimated endgame playout in the analyzer macondo v0.13.3 added EndgameMove.is_estimated, which marks moves past the endgame solver's search horizon. Beyond that point the solver plays the position out greedily, so the tail of a variation -- and the final spread it produces -- is a plausible continuation rather than a proven line. The analyzer rendered every move identically, so "best spread: +42" read as authoritative even when it came entirely from a playout. Vendor the new field for TS generation. No Go changes are needed: the vendored copy keeps macondo's go_package, so the backend already round-trips field 4 through upstream types. In the analyzer: - Insert a labeled dashed rule before the first estimated move. Estimated moves are always a contiguous tail, so one divider marks the boundary; when nothing was searched it lands at the top and reads as a caption. - Prefix the spread with a tilde-approx sign on both the PV header and each alternative line when that variation contains estimated moves. Estimated moves keep full contrast and stay clickable -- dimming and italics already mean "ignored" in the sim table. Numbering moved from <ol> to explicit N) so the divider can be a list item without consuming a counter, which renders identically since move_number is always i+1. The omgwords regeneration is an unrelated one-line comment catch-up left stale by the earlier SLV lexicon commit. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

Commit:572ebf4
Author:César Del Solar

add slovene

Commit:b696f68
Author:César Del Solar

Correct the stream_slot_count comment: deletion warns, re-pointing does not The comment claimed the UI warns before 'deleting or re-pointing' a slot somebody is streaming from. Only deletion warns. Re-pointing deliberately does not, and shouldn't: it happens to every slot every round, and it is not a hazard but the mechanism — stream slots mirror a broadcast slot precisely so directors can move it and everyone downstream follows. Deleting is rare and permanently breaks configured browser sources, which is why that one asks. (The confirm that does fire on reassign is pre-existing and unrelated: it warns when the slot's current game is still being annotated, and never consults stream_slot_count.) Comment only; no behaviour change. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

Commit:f5e74a3
Author:César Del Solar

Correct 'latest annotated game' wording in code comments too Same correction as the manual, applied to the source of truth: the proto enum, the migration, the query, and the OBS handler all described latest_annotation as following the most recently *edited* game. It orders by games.updated_at, which a move does not bump — SendGameEvent only upserts game_documents. So it is the most recently *started* game, and returning to an older one does not bring it back to the front. The note on GetLatestAnnotationForUser also records what fixing it would take, since game_documents has no timestamp column at all today. Comments only; no behaviour change. Generated files follow the proto. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

Commit:464c91f
Author:César Del Solar

Add stream slots: permanent OBS URLs that outlive a broadcast An OBS browser source points at one URL forever, but a broadcast slot's URL embeds the broadcast slug, so it dies with the event. That breaks two real workflows: alternating one live stream between two concurrent events, and running a recurring stream that covers a different tournament every few weeks. Both currently mean editing every browser source in the scene. A stream slot is a user-owned pointer whose URL carries only the owner's username and the slot name: /api/annotations/obs/user/<you>/<slot>/<field> It resolves through a typed target, so one slot can follow different things over its life without the URL ever changing: none - renders the placeholder broadcast_slot - mirrors an event's slot, following whatever table that event features latest_annotation - follows the owner's most recently edited annotated game Pointing at a broadcast slot rather than straight at a table is what makes alternating work: each event's own directors keep their slot current while you are away, so cutting back lands on what that event features now. Keeping the target kind on the slot rather than in the URL is what lets someone stream club nights off their own annotations and later graduate to a broadcast without touching OBS. latest_annotation joins through broadcast_games, so when the game it lands on was claimed from a broadcast the tournament standings fields resolve too, and opponent_name works there since the tracked player is well defined. This replaces the old two-segment alias URL (/api/annotations/obs/user/<username>/<field>), which had no persistence and resolved to "whichever game this user last touched" - ambiguous once a broadcast has several annotators. Requests in the old shape now return an error naming the replacement, which renders inside the browser source itself. Also fixes a pre-existing frozen-overlay bug. The SSE pages only repaint on a message, and the broadcast callback stayed silent when a slot stopped resolving to a game - so reassigning a slot to an unclaimed table, or unclaiming a game, left the previous game on screen indefinitely while a fresh load of the same URL correctly showed the placeholder. Clearing is now an explicit push. Slot deletion also notifies, so those streams clear instead of freezing forever. Adds an in-app manual covering both audiences, opened from every place a slot is configured, and reorganizes the director panel into tabs. That panel had grown to four flat sections in one scroll, with the two most confusable concepts adjacent and near-identical, and ~80 lines of duplicated JSX between Annotators and Directors. Not yet exercised against a live database: the migration has not been applied and the end-to-end switching behavior is unverified. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

Commit:b4127f5
Author:César Del Solar
Committer:GitHub

Merge pull request #1936 from woogles-io/new-tournament-irl-toggle tournament editor: IRL mode on create, slug link, and slug validation

Commit:6a26de6
Author:César Del Solar

Expose CreateDivision RPC for league admins ManualDivisionManager.CreateDivision already existed and is used internally by the season rebalancer, but there was no way to call it directly - only DeleteDivision and MovePlayerToDivision were wired up. Adds the RPC (mirroring DeleteDivision's shape and guards: requires can_manage_leagues, only allowed while the season is SCHEDULED) so admins can manually split an oversized division without going straight to the database. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Commit:b107dd0
Author:Andy Kurnia

add mistake_index to PlayerSeasonGame Add an optional mistake_index field to the PlayerSeasonGame message so a player's per-game BestBot "mistake score" can be surfaced in the league game-history modal. It is optional (proto3 presence) so an unanalyzed game is distinguishable from a genuine 0 (a perfect game). Regenerated Go and TypeScript bindings. Co-Authored-By: Kofi Bingo (Claude Opus 4.8, 1M context) <noreply@anthropic.com>

Commit:2c7d27d
Author:Andy Kurnia

add mistake_index to PlayerSeasonGame Add an optional mistake_index field to the PlayerSeasonGame message so a player's per-game BestBot "mistake score" can be surfaced in the league game-history modal. It is optional (proto3 presence) so an unanalyzed game is distinguishable from a genuine 0 (a perfect game). Regenerated Go and TypeScript bindings. Co-Authored-By: Kofi Bingo (Claude Opus 4.8, 1M context) <noreply@anthropic.com>

Commit:c3f5723
Author:Andy Kurnia

add irl_mode field to NewTournamentRequest Allows the IRL (over-the-board) pairing mode to be set at tournament creation time instead of only afterward via SetTournamentMetadata. Regenerated Go and TypeScript bindings. Co-Authored-By: Kofi Bingo (Claude Opus 4.8, 1M context) <noreply@anthropic.com>

Commit:cf5f55c
Author:César Del Solar

puzzles: weekly PvP generation, tag info-leak fix, retry-without-spoiler - Split puzzle tags into category_tags (always sent) and descriptive_tags (only after a finalized attempt) to stop leaking solution shape via GetPuzzle before the user solves. - Add include_solution flag to PuzzleRequest so GetPuzzle no longer auto-reveals the answer on a previously-solved puzzle; the frontend now auto-enters retry mode instead of requiring a "Try Again" click. - Switch puzzle candidate queries to ORDER BY RANDOM() (with a matching functional index on games) for even weekly sampling, and drop the unused OFFSET parameter. - Loosen puzzle fetch transactions from RepeatableRead to the default ReadCommitted isolation, since no multi-statement snapshot was needed. - Add earliest_start_date to cap how far back PvP puzzle generation walks. - Add a weekly-puzzle-gen maintenance task with per-lexicon bucket/limit configs (English, French, German) mirroring historical one-off runs. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Commit:9fca8dd
Author:César Del Solar

league: add DeleteDivision RPC and admin UI for cleaning up empty divisions Exposes the existing ManualDivisionManager.DeleteEmptyDivision helper as an admin-only RPC (fails if the division still has players) so a division that was manually emptied via MovePlayerToDivision can be deleted and the remaining divisions renumbered, without a direct DB edit. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Commit:a240174
Author:César Del Solar
Committer:GitHub

Merge pull request #1888 from woogles-io/player-season-games-clocks show live clocks in the league player season-games modal

Commit:3ac6f59
Author:César Del Solar
Committer:César Del Solar

refactor: gate frontend UI on granular permissions instead of JWT role codes Replaces three uncoordinated permission systems (JWT short codes adm/mod/toc, per-component getSelfRoles checks, and getModList) with a single source of truth: the user's effective granular permission codes fetched via a new GetSelfPermissions RPC and stored in loginState.permissions. - Add GetUserPermissions SQL query and rbac.UserPermissions Go helper - Add GetSelfPermissions RPC to AuthorizationService (proto + handler) - Remove adm/mod/toc short-code mapping from socket JWT (perms claim retired) - Replace loginState.perms with loginState.permissions (Array<string>) - New hasPermission(permissions, Perm.*) helper with admin_all_access wildcard - Migrate all ~15 UI gating sites to granular permission checks; fixes Broadcast Creator role holders never seeing the New Broadcast button - Remove per-component getSelfRoles gating in leagues, tournaments, seek form - Guard /admin route client-side on AdminAllAccess permission Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

Commit:075c3d1
Author:Andy Kurnia

return the live clock with in-progress league season games GetPlayerSeasonGames returned no timing for in-progress games, so a viewer of a player's season games could not see their live correspondence clocks. Add last_update, increment_secs and on_turn_time_bank_ms to PlayerSeasonGame, populated for in-progress games by loading each game's entity (a player has at most a handful of in-progress league games and this runs only on modal open, so the per-game load is fine). Whose turn it is is already encoded in the result field, so the client can name the on-turn player without an extra field. Co-Authored-By: Kofi Bingo (Claude Opus 4.8, 1M context) <noreply@anthropic.com>

Commit:c886ec0
Author:Andy Kurnia

store the audraw reset_round 0-based to match the rest of the code reset_round was the lone 1-based round field; every other round (the RoundControls.Round, the RepeatRounds history, the pairing engine) is 0-based. Storing it 1-based forced a `-1` conversion and a `< 1` clamp in the dispatcher, and made the proto3 zero-default an invalid sentinel. Make reset_round 0-based: the dispatcher uses it directly as the round threshold, the proto3 zero-default (0) naturally means "avoid every prior meeting" (the strict default), and the clamp and conversion both go away. The frontend stores it directly too (no offset). No migration is needed: no production tournament has used the Australian Draw yet. Updates the proto/generated doc comments and the reset tests to the 0-based convention (the test reset_round values shift 9->8, 7->6, 1->0, describing identical thresholds, so every expectation is preserved). Co-Authored-By: Kofi Bingo (Claude Opus 4.8, 1M context) <noreply@anthropic.com>

Commit:973b901
Author:César Del Solar
Committer:GitHub

Merge pull request #1870 from woogles-io/australian-draw-be add the australian draw tournament pairing method

Commit:fade188
Author:Andy Kurnia

add the australian draw tournament pairing method Adds the Australian Draw pairing method: a transparent, hand-playable, deterministic alternative to the matching-based methods. Round 1 is a casement draw (top half vs bottom half); every later round pairs the top remaining player with the highest-standing legal opponent that still leaves the rest of the field pairable, backtracking when a choice paints the draw into a corner, so a repeat-free draw is always found when one exists. A new 1-based reset_round round control (proto field 20) lets a director forgive meetings before a day boundary: a meeting in round r is avoided iff r >= reset_round (1 = avoid all prior, the default; >= current round = King-of-the-Hill). When a strict pass cannot pair the field the reset point is relaxed one round at a time, then errors cleanly if still impossible. An odd field is paired by padding it with a phantom bye at the bottom of the standings and running the even algorithm; whoever the phantom is paired with takes the real bye, unifying the odd and even cases with no special-casing. Backend and generated proto only; the director-UI changes are a separate follow-up PR. Co-Authored-By: Kofi Bingo (Claude Opus 4.8, 1M context) <noreply@anthropic.com>

Commit:63fde47
Author:Andy Kurnia

expose live time bank in active correspondence game lists The active-game payloads carried incrementSecs/lastUpdate/playerOnTurn but not the live consumed time bank, only the initial bank config. That left clients unable to compute the real time until a player times out (per-turn allowance + remaining bank), so league games -- which have banks -- could not be sorted or flagged correctly. Add a repeated int64 time_bank field to GameInfoResponse carrying the current per-player bank remaining in milliseconds, and populate it in all three active-correspondence builders in the game DB store. The two user-facing queries (ListActiveCorrespondenceGamesForUser and ...ForUserAndLeague) did not select the timers column, so add it there; ListActiveCorrespondenceGames already loaded it. Regenerated sqlc models and the buf proto bindings (Go + TS). Co-Authored-By: Kofi Bingo (Claude Opus 4.8, 1M context) <noreply@anthropic.com>

Commit:66c0339
Author:César Del Solar

feat: add RevokeUserFromLeagues RPC and UI for league promoters Adds a new can_revoke_from_leagues permission (migration 202606030001) assigned to League Promoter and Manager roles. Implements the RevokeUserFromLeagues RPC in the league service, and extends the invite widget with a revoke section. Also adds a League Administration link on the leagues list page for users with promoter/admin access. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Commit:26f2995
Author:César Del Solar

fix(broadcasts): OTel spans for RPC cache, OBS transparent default, editor nav, annotation_done, NATS user ID - Add OTel trace spans to cachedFetch (cache.name, cache.hit attributes) - Default OBS URL builder transparent background to on - Show "Back to broadcast" link in editor when game is part of a broadcast - Hide "Unclaim" button when annotation is already done (add annotation_done to GetBroadcastGameContextResponse) - Fix SendGameEvent publishing NATS activity on in-game player ID instead of annotator UUID Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Commit:601b631
Author:César Del Solar

feat(broadcasts): full game dashboard with archive, standings, highlights, and split feed endpoint Adds a complete broadcast viewer dashboard with tab navigation (Pairings, Standings, Live Now, Recently Completed, Archive, Highlights), per-game stats materialized into broadcast_games, and a separate GetBroadcastAllGames RPC for the Archive tab so the heavy feed-walk payload is only fetched when needed. Archive shows every game (annotated + TSH feed-only) with Went First / Went Second columns derived from p12 feed data. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Commit:9bc56cc
Author:jvc56

Don't need to reserve since changes aren't in prod

Commit:e738d2f
Author:jvc56

More AI review

Commit:13f2dbe
Author:jvc56

Expand Pairings API

Commit:cd41862
Author:César Del Solar

Add FailJob RPC so volunteer workers can report corrupt games Add FailJob to AnalysisQueueService proto so macondo workers can immediately mark a job as permanently failed when they detect unrecoverable data corruption (e.g. a game with an 8-tile rack), instead of silently dying and letting the job waste its 3-attempt retry budget on other volunteers before ending with a generic timeout message. The existing unused FailJob SQL query (analysis_jobs table) is now wired up through the new connect-rpc handler in pkg/analysis/service.go. Auth is via X-Api-Key; the job must be claimed by the calling worker. Error messages are truncated to 1 KB and surfaced via GetAnalysisStatus (error_message field), which is already exposed to clients. Also regenerates all buf-generated Go/TS files (minor version drift). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Commit:4b7bfbd
Author:César Del Solar
Committer:César Del Solar

Copy division names, game settings, and round controls from source tournament When copying a tournament, the wizard now fetches per-division data (game request and round controls) via GetTournamentMetadata and applies them on creation. If all divisions share the same game settings they are copied as the global request; if they differ a per-division copy is stored and a note is shown to the user. Adds TournamentDivisionSummary to the proto and GetRoundControls() to the DivisionManager interface. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Commit:293e7bc
Author:Claude
Committer:César Del Solar

Add self-serve tournament creation with wizard UI - Add /tournaments route with step-by-step tournament creation wizard - Step 1: Choose IRL vs online mode, configure monitoring - Step 2: Set name, slug, schedule, description (markdown) - Step 3: Configure divisions and game settings (lexicon, time, etc.) - Step 4: Review and create - Add tournament copy feature: directors can copy settings from their past tournaments to quickly create similar events - Add rate limiting: max 3 tournament creations per week for regular Tournament Creator role; unlimited for Tournament Manager role - Add 'toc' permission to JWT for Tournament Creator/Manager roles so frontend can gate the creation UI - Add GetMyTournaments RPC to fetch tournaments where user is director - Add CountRecentTournamentsByUser and GetTournamentsByDirector to tournament store for rate limiting and copy features https://claude.ai/code/session_013uEnLRsznYmyUUbrTumdeX

Commit:ee9fbad
Author:César Del Solar
Committer:César Del Solar

improve broadcasts: polish, safeguards, renames, and sorting - Director panel: remove Active toggle, add section dividers - LIVE badge: hide for finished (scoresFinalized) annotations; show UPCOMING when pollStartTime is in future - Broadcasts list (/broadcasts): sort live first, upcoming next (soonest first), past archived at bottom (last 5); add GetAllBroadcasts RPC - Announcements widget: split broadcasts into live/upcoming (≤14 days) sections; show up to 3 past broadcasts - Back to Broadcast button in gameroom topbar for annotated games (mirrors Back to League/Tournament) - UnclaimGame: block unclaiming a game that already has a complete annotation - End-time validation in Create/Edit broadcast forms (end must be after start) - Rename omgwords "broadcast" terminology to "annotated": CreateAnnotatedGame, DeleteAnnotatedGame, AnnotatedGamesResponse, etc. - Broadcast slots: migration, SQL queries, proto messages, Go service, and UI wiring - OBS integration: obs.go and obs_handler.go stubs Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Commit:ef77286
Author:César Del Solar

fix broadcast socket notifications and unclaim flow - Fix NATS topic prefix: channel-broadcast-{slug} → channel.broadcast-{slug} so messages match the socket server's channel.> subscription and actually reach viewers on the broadcast page - Add BROADCAST_GAMES_UPDATED (type 53) proto message and event, distinct from BROADCAST_UPDATED, for when annotation state changes (claim/unclaim) rather than feed data; frontend only invalidates GetBroadcastGames on receipt - Emit BROADCAST_GAMES_UPDATED from ClaimGame and UnclaimGame RPCs so viewers see annotation column update instantly without waiting for next poll - Replace "Delete this game" with "Unclaim this game" in the editor for games that are part of a broadcast; calls UnclaimGame and navigates back to the broadcast page on success Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Commit:21d44b7
Author:César Del Solar

add live broadcasts feature Introduces the full broadcast pipeline: TSH feed polling, game annotation claiming, and a React UI for viewers, annotators, and directors. - New `broadcasts` service with TSH/newt JSON feed parser, in-memory cache, background poller (respects poll_interval_seconds / start+end time window), and TriggerPoll RPC for immediate refresh - DB: broadcasts, broadcast_games, broadcast_annotators, broadcast_directors tables; migration 20260402000001 - RBAC: `can_create_broadcasts` permission + Broadcast Creator role - IPC: BroadcastUpdatedEvent proto + NATS publishing on each poll cycle - Frontend: BroadcastsList, BroadcastRoom (division/round selector, 30s poll, annotation column), CreateBroadcast, EditBroadcast, BroadcastAnnotatorPanel, BroadcastDirectorPanel; broadcast context (name/div/round/table) shown in GameInfo widget for /anno and /editor routes - gitignore .env and .env.production; add conn-db.sh utility Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Commit:7e9e29d
Author:César Del Solar

fix legacy analyses

Commit:db34c62
Author:César Del Solar
Committer:César Del Solar

BestBot Analysis v2: interactive moves, endgame improvements, UI polish - Click sim/PEG/endgame/played/best moves to place tiles on board - Through-tile parenthesized display (e.g. QUI(T)c) in all move rows - parseMoveDescription: parses moveDescription strings into AnalyzerMove - Endgame: virtual board threads through sequence for correct through-tile resolution on moves 2+; deduplicate PV from alternative lines - SimPlaysTable: coord/move split columns, expandable ply details with per-play iteration count, σ notation for score stdev - PEGPlaysTable: coord/move split, alphabetically sorted outcomes, brighter green color, larger outcome font - Sort sim plays client-side (win% desc, equity tiebreak) to fix Pass ordering edge case from server-side extractTopSimPlays - Mistake Score help: 0.25% noise floor, BestBot disclaimer paragraph - Sync vendored macondo proto: add iterations/is_ignored fields, regen Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Commit:38c9f4e
Author:César Del Solar
Committer:César Del Solar

v2 progress

Commit:d3dbb62
Author:Andy Kurnia

add per-season h2h game details to protobuf Co-Authored-By: Kofi Bingo (Claude Opus 4.6, 1M context) <noreply@anthropic.com>

Commit:6badf93
Author:César Del Solar

Add known_opp_rack field to vendored macondo proto Update vendored macondo proto to include the known_opp_rack field (field 22 in TurnAnalysis message). This field tracks opponent tiles revealed by challenged phonies during game analysis. This fixes the protobuf version mismatch that was causing volunteer workers to fail when submitting analysis results. Workers running newer macondo versions were sending results with this field, but the server's older proto definition didn't recognize it, causing "unknown field knownOppRack" errors. Changes: - Added known_opp_rack field to TurnAnalysis message in vendored proto - Regenerated Go and TypeScript proto files using go generate Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

Commit:42fc000
Author:Claude

Add head-to-head records to league roster and games list Add a new GetPlayerLeagueH2H API endpoint that queries a player's lifetime head-to-head record (W-L-D, spread) vs all opponents across all seasons of a league. Uses the game_players table with the idx_game_players_player_league_season partial index for efficient lookups. - League roster: Shows H2H column with the logged-in user's record against each player, color-coded green/red for winning/losing records - League games list: Shows lifetime record (W-L-D +spread) next to each opponent's name in the correspondence games sidebar https://claude.ai/code/session_01HVkPnN2U5MKQMidhNQPC82

Commit:0423553
Author:César Del Solar
Committer:GitHub

Merge pull request #1735 from woogles-io/league-roster-history add league roster view showing all players across seasons

Commit:9d1d03c
Author:Andy Kurnia

add GetLeagueRoster RPC for all players across all seasons new RPC returns every player who has ever played in a league with their division, record, rank, and result per season. rank is computed server-side using canonical standings sort. players registered but not yet placed return division_number=0. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

Commit:9ebc12e
Author:Andy Kurnia

tighten league rank bounds using max-flow on actual pairings move rank computation from frontend to backend where it has access to the actual remaining game pairings. uses edmonds-karp max-flow to determine simultaneous feasibility: - worst rank: maximize players that can simultaneously surpass you, constrained by the game graph (a win for one player is a loss for the other) - best rank: minimize players forced above you by checking if within-set game points can be absorbed without exceeding your score adds best_rank/worst_rank fields to LeaguePlayerStanding proto, a lightweight GetUnfinishedDivisionGames query, and removes the O(n^2) client-side computation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

Commit:954ad14
Author:César Del Solar

real-time notification of analysis done

Commit:1d5d039
Author:César Del Solar
Committer:César Del Solar

requeue analysis support

Commit:ae82350
Author:César Del Solar
Committer:César Del Solar

add mistake index and analysis service improvements / dashboard improvements

Commit:722aa99
Author:César Del Solar
Committer:César Del Solar

fadd analysis dashboard

Commit:5906e57
Author:Andy Kurnia

Merge branch 'master' into league-game-end-reason

Commit:0c0ccb1
Author:César Del Solar
Committer:César Del Solar

add some more testing

Commit:4b0537b
Author:César Del Solar
Committer:César Del Solar

clean up vendoring a little bit

Commit:b139055
Author:César Del Solar
Committer:César Del Solar

analysis-service progress

Commit:3005d18
Author:Andy Kurnia

return game end reason for league games

Commit:dfb790a
Author:César Del Solar

force-forfeit league cheaters

Commit:3dbccc6
Author:Andy Kurnia

show league turn

Commit:34a4db0
Author:César Del Solar
Committer:César Del Solar

fix force finished league games

Commit:ebb5291
Author:César Del Solar

add wespa titlists, refactor org assignment some more

Commit:a8a74e9
Author:Claude

Add optional credentials support to admin organization assignment - Add optional credentials field to ManuallySetOrgMembershipRequest proto - Update ManuallySetOrgMembership to accept and use credentials if provided: - If credentials provided: use authenticated fetch (validates creds) and store encrypted credentials for future title refreshes - If no credentials: use public API/database (existing behavior) - Update admin frontend to show optional credential fields for NASPA/ABSP - Credentials are stored encrypted so titles can be refreshed automatically

Commit:0809981
Author:César Del Solar

make 5-pt challenge per word in editor

Commit:0d5812e
Author:César Del Solar

Fix opponent rack sync and enhance tile availability errors Changes: - Add opponent_rack field to ServerOMGWordsEvent and ServerGameplayEvent to keep frontend in sync when rack inference borrows tiles from opponent - Backend now populates opponent_rack for annotated games - Frontend updates opponent's rack from socket messages - Enhanced error messages for tile availability to be user-friendly (e.g., "the tile I is not available" instead of technical error) - Added debug logging for tile borrowing in TileInventory - Removed board mismatch comparison logging (not useful for challenged moves) This fixes the issue where opponent's rack would show phantom tiles after rack inference until page refresh. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

Commit:fffd47d
Author:César Del Solar

add a maintenance overlay and pause real-time games when deploying

Commit:2d1033c
Author:César Del Solar

show warnings for players with low time banks in a division

Commit:a6cd5f6
Author:César Del Solar

prototype socket subscription approach

Commit:80b1698
Author:César Del Solar

add time bank api call

Commit:194d412
Author:César Del Solar

fix: update league routing and announcements tab order - Changed league link path from `/league/${league.slug}` to `/leagues/${league.slug}` in announcements.tsx. - Moved the "Announcements" tab to appear after "Events" in AnnouncementsWidget. refactor: modify end-of-season outcome handling - Updated MarkSeasonOutcomes to preserve placement_status while only updating previous_division_rank. - Adjusted related functions and comments to reflect the new logic. test: enhance end-of-season tests for rank verification - Updated tests to verify that previous_division_rank is set correctly for players in both divisions. - Adjusted assertions to check standings outcomes based on new logic. chore: adjust rebalance scoring system - Increased priority score constants for placement statuses to improve ranking accuracy. - Updated the scoring formula to reflect new multipliers and ensure correct calculations. feat: add new promotion formula for divisions - Introduced a new promotion formula (PROMO_N_DIV_3) to allow for more flexible promotion calculations based on division size. fix: ensure placement status is included in league standings - Modified GetAllDivisionStandings to include placement status in the returned standings data. chore: update database queries for previous division rank - Added new query to update previous_division_rank in league_registrations. - Updated related models and store interfaces to accommodate this change.

Commit:c589a86
Author:César Del Solar

feat: add GetRecentSeasons RPC to LeagueService - Introduced LeagueServiceGetRecentSeasonsProcedure for the new GetRecentSeasons RPC. - Updated LeagueServiceClient interface to include GetRecentSeasons method. - Implemented GetRecentSeasons in leagueServiceClient. - Added handler for GetRecentSeasons in NewLeagueServiceHandler. - Included unimplemented handler for GetRecentSeasons in UnimplementedLeagueServiceHandler. - Rearranged lobby to put chat on left again

Commit:c5ff31c
Author:César Del Solar

Extended league standings

Commit:0f20308
Author:César Del Solar

add tou export to tournaments

Commit:cba3bdd
Author:César Del Solar

fix various timing and other bugs

Commit:b617947
Author:Claude
Committer:César Del Solar

refactor league scheduling: hourly runner, editable dates, new promo formula Major changes: - Add UpdateSeasonDates RPC to allow editing season start/end dates from admin - Refactor league tasks from midnight/8am to hourly runner with idempotency - Add task tracking columns (closed_at, started_at, etc.) to prevent duplicate runs - Spread game creation over time with batching (150ms delay per 10 games) - Change promotion/relegation formula from ceil(N/6) to ceil((N+1)/5) The hourly runner uses actual season start/end dates instead of fixed times, allowing different leagues to start at different times (e.g., CSW at 8am UTC, NWL at 8am Eastern).

Commit:ecac915
Author:César Del Solar

organization integrations and titles

Commit:d314888
Author:César Del Solar

feat: add SeasonZeroMoveGames and SeasonPlayersWithUnstartedGames responses to league service - Introduced SeasonZeroMoveGamesResponse and ZeroMoveGame message types in league_service.proto. - Added SeasonPlayersWithUnstartedGamesResponse and PlayerWithUnstartedGames message types. - Updated LeagueService with new RPC methods: GetSeasonZeroMoveGames and GetSeasonPlayersWithUnstartedGames. - Implemented client and handler methods for the new RPCs in league_serviceconnect.

Commit:d9af2c4
Author:César Del Solar

allow moving league players between divisions for admins while seasons are SCHEDULED

Commit:6e596d9
Author:César Del Solar

Add UpdateLeagueMetadata RPC to LeagueService - Introduced the UpdateLeagueMetadata procedure in the LeagueService. - Updated LeagueServiceClient interface to include UpdateLeagueMetadata method. - Implemented UpdateLeagueMetadata in leagueServiceClient. - Added handler for UpdateLeagueMetadata in LeagueServiceHandler.

Commit:9c9cb39
Author:César Del Solar

Update generated protobuf files and enhance game state management - Updated generated files for various protobuf definitions to reflect changes in protoc-gen-es version from 2.10.0 to 2.10.1. - Added initial time bank management for correspondence games in the game reducer and timer controller. - Enhanced the ExaminableStore to calculate and manage time bank history for correspondence games. - Modified the GameHistoryRefresher to include initial time bank minutes. - Updated SQL queries and models to streamline game and league data retrieval, removing unnecessary structs.

Commit:0ff3c27
Author:César Del Solar

Remove executive_director field from tournaments Fixes #1478 The executive_director field was unused metadata that has been replaced by the directors field which allows multiple directors. Changes: - Add migration to drop executive_director column from tournaments table - Remove field from protocol buffer definition (marked as reserved) - Remove from all Go backend code (entity, models, stores, tournament) - Regenerate protocol buffers (Go and TypeScript) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>

Commit:1e8aa99
Author:César Del Solar
Committer:César Del Solar

fix #836; confirm email address on acct creation

Commit:86ad6e8
Author:César Del Solar
Committer:César Del Solar

feat: Implement user invitation to leagues - Added InviteUserToLeagues RPC in LeagueService to allow users to invite others to participate in leagues. - Introduced InviteUserRequest and InviteUserResponse message types in the league service proto definition. - Updated LeagueServiceClient and LeagueServiceHandler interfaces to include the new invitation method. - Implemented permission checks for inviting users and role assignment for league access. - Enhanced chat functionality to support league channels in Redis chat store. - Updated user service to accommodate league ID in active chat channel requests. - Adjusted database queries and models to support new league-related functionalities.

Commit:a7f4395
Author:César Del Solar
Committer:César Del Solar

feat: Add league support for correspondence games - Updated ActiveGame type to include leagueSlug for league games. - Enhanced socket handlers to process OUR_LEAGUE_CORRESPONDENCE_GAMES messages and dispatch them to the lobby context. - Implemented league correspondence games retrieval in the Bus service. - Created a new query to fetch player season games, including scores and opponent details. - Modified game metadata retrieval to include league information. - Updated game-related SQL queries to include league and season IDs. - Added league ID and slug fields to GameInfoResponse for better game context. - Enhanced PubSub to handle league messages and forward them to the appropriate realms.

Commit:f26f258
Author:César Del Solar
Committer:César Del Solar

Refactor league standings and player management - Updated the logic for calculating the highest division number in standings.go to remove the rookie division check. - Removed the UpdateDivisionPlayerCount method from the mockLeagueStore and DBStore interfaces as it is no longer needed. - Modified the CreateDivision SQL query to exclude player_count, reflecting changes in the LeagueDivision model. - Removed player_count from the LeagueDivision struct in models.go. - Updated protobuf definitions to remove graduated placement status and added new messages for player season games. - Implemented GetPlayerSeasonGames RPC in the LeagueService, including request and response message structures.

Commit:73f6260
Author:César Del Solar
Committer:César Del Solar

feat: add memorial and FAQ sections to leagues UI - Introduced a memorial section honoring Elliott Manley in leagues list. - Added a Frequently Asked Questions (FAQ) section with collapsible panels to explain league mechanics, promotion/relegation, time banks, and rookie leagues. - Enhanced styling for the new sections in leagues.scss. refactor: update game state management to include time bank - Modified game reducer to handle time bank for players during gameplay. - Updated timer controller to manage time bank states for both players. - Adjusted game history refresher to include time bank information for both players. fix: streamline game end process and league data handling - Simplified the game end process by removing unnecessary checks for league store. - Enhanced game creation logic to properly handle league-related data during game instantiation. - Updated database interactions to include league, season, and division IDs for games. chore: update protobuf definitions for time bank management - Added time bank fields to GameHistoryRefresher and ServerGameplayEvent in protobuf definitions. - Ensured proper serialization and deserialization of time bank data for correspondence games.

Commit:be1beb0
Author:César Del Solar
Committer:César Del Solar

lots of progress

Commit:6daf0c4
Author:César Del Solar
Committer:César Del Solar

league progress

Commit:474de87
Author:César Del Solar
Committer:César Del Solar

feat: add LeagueService client and handler implementation - Generated LeagueService client and handler for managing leagues, seasons, and player statistics. - Implemented methods for creating, retrieving, updating leagues, and managing seasons. - Added support for division standings and player league history. - Included unimplemented handler for methods not yet defined.

Commit:3d2cf4b
Author:César Del Solar

show finished correspondence games

Commit:99a6588
Author:César Del Solar

show next game button for corres games

Commit:dc3ea34
Author:César Del Solar

make read-only director -1, redo ghetto tools ui

Commit:6640517
Author:César Del Solar

upcoming tourneys widget

Commit:c3d1641
Author:César Del Solar

updates to make sharing more seamless

Commit:786588c
Author:César Del Solar

vdo ninja integration for tournaments

Commit:2dace25
Author:César Del Solar

feat(tournament): enforce COP pairing rules and improve UI for score editing - Added error message for using non-COP pairings after COP pairings in constants. - Refactored round controls UI to improve layout and added note about COP gibsonization. - Enhanced score editing functionality for both paired and self-paired players, allowing directors to edit scores directly from the UI. - Introduced CSS styles for score edit triggers to improve user experience. - Implemented validation in the ClassicDivision logic to ensure that once COP pairing starts, all subsequent rounds must also use COP pairing. - Updated error handling to include new error for non-COP pairings after COP.

Commit:a6fcad8
Author:César Del Solar

show cop gibsonization on FE

Commit:721ddb9
Author:César Del Solar

refactor ghetto tools

Commit:877a568
Author:César Del Solar

complete integration

Commit:8992734
Author:César Del Solar

API request to unfreeze games

Commit:4ceb19b
Author:César Del Solar

fix time bank behavior and make adjudication queries more efficient

Commit:45166cf
Author:César Del Solar

fix sending seeks to established players

Commit:bb85fed
Author:César Del Solar

send seeks only to established ratings option

Commit:f0ac191
Author:César Del Solar

qol fixes etc

Commit:635dd98
Author:César Del Solar

correspondence mode

Commit:d34ade6
Author:César Del Solar

a sketch of sorts; will likely need to be redone after upgrading db tables

Commit:d83c906
Author:César Del Solar

Add new efficient presence notification system - Add --new-presence-system feature flag for backward-compatible rollout - Add GetFollowersRequest/Response protobuf messages - Add broadcastPresenceChanged() for O(1) presence notifications - Add getFollowers NATS request handler - Maintain backward compatibility with existing system - Fix slow consumer issue by eliminating circular dependency The new system publishes single message per presence change to presence.changed.<userID> instead of creating individual messages for every follower. This eliminates the channel blocking that caused "slow consumer, messages dropped" errors during high activity. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>