feat(ui): live trading leaderboard - #352
Conversation
Replaces the Daily Leaderboard tab with a two-week Live Season board. The season engine is not built, so the tab renders in preview: real curves from the Competition window, full season chrome, and a banner saying no season has run. Preview is detected by comparing the period requested against the period returned -- `_normalize_period` coerces an unknown period back to `contest`, so a check against the response alone cannot see it. `?view=daily` / `#daily` and saved `competitionTab: 'daily'` alias to the season tab; an unrecognised tab matches no panel and paints Competition blank. Payload contract and the decisions behind it: docs/superpowers/specs/2026-08-15-live-season-leaderboard-ui.md Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L9cV2UzLgdTVmwcHLAtJeA
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
The Daily Leaderboard tab is gone, so the 22:30 UTC cron would keep deploying every competition LLM nightly, billable, for a board with no surface. Endpoint, secret and workflow_dispatch all stay -- the season engine's nightly advance is this same call. Adopts the pause from #335 so this PR supersedes it rather than conflicting with it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L9cV2UzLgdTVmwcHLAtJeA
Overlaps #335 — this PR supersedes it@JinBoatus1's #335 (chore(leaderboard): temporarily hide Daily Leaderboard) reaches the same conclusion by a lighter route: hide the tab, redirect
Every region #335 touches, this PR also touches, and the test file is a rename/modify conflict. #335 is also cut from an older base ( Suggested ordering: close #335 in favour of this one — its cron pause is now carried here, and its "re-enable later" path is superseded by the season board actually shipping. That is Jinbo's call, not mine; if #335 should land first instead, say so and I will rebase this onto it. |
#335 hid the Daily Leaderboard subtab and paused its nightly cron. This branch removes that tab outright and puts the season board in its place, so every region #335 touched is rewritten here rather than combined: - nav map / migrateSavedNavState: 'daily' now aliases to the season tab instead of falling through to Competition, so a deep link lands on the successor board rather than silently on a different one. - showCompetitionPanel / navigateToPage / viewParamForNavState: same, plus the 'live' key. - the subtab and the About card are deleted, not hidden. - test_frontend_daily_leaderboard.py: deleted with the tab it pinned. Its one new case asserted the subtab carried `hidden`; the replacement file asserts the tab is absent from the UI entirely, which is strictly stronger. The other assertions were preserved through the rename. - cache-buster floor keeps this branch's higher values (110/111), which clear main's merged 102/101. The cron pause is kept as #335 wrote it, with the comment rewritten to say why the schedule is off now: the board it fed no longer exists, and the season engine's nightly advance is this same endpoint. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L9cV2UzLgdTVmwcHLAtJeA
Settles the two naming questions the grilling session left open, and fixes two honesty bugs the name change exposed. Naming - The board is the **Live Trading Leaderboard** (was the working title "Live Season"). The tab, the board title and the About card all use it. - The tab key and API period become `live`. `season` and `daily` stay as aliases in NAV_VIEW_MAP, showCompetitionPanel, migrateSavedNavState and loadLeaderboardData, so ?view= deep links and saved localStorage from either earlier name land on the successor board. An unrecognised competitionTab matches no boot-CSS rule and no panel, so the failure mode being avoided is a blank Competition page, not a wrong tab. - The current season is Season 0, the shakedown season; Season 1 is the first that counts. Season 0 is falsy `season?.number ? ... : 'Season —'` renders Season 0 as *no season at all* — silently, and only for the season shipping right now. Both render sites now go through displayedSeasonNumber(), which does an explicit Number.isFinite check. Guarded by a source-shape test, because a test that passes 3 cannot see this bug. Two claims the UI was making and should not have been - The subtitle fell back to `window.start_date` for its "last completed" date, so the board printed "last completed 2026-04-15" — a specific, plausible, entirely invented advance date sitting directly under the banner saying no season has run. That fallback is gone; only daily_status carries a real advance, and preview suppresses it outright. - The season strip said "Next advance: nightly after the 16:00 ET close" unconditionally, promising a job that is not deployed. In preview it now reads "No advance scheduled". "Live" is itself a claim, so the About card qualifies it: simulated trading on real market data, no broker account, no real capital, no order leaving the system. PR #328 puts brokered execution in non-goals and execution/paper_backend.py is still a stub, so a board named for live trading that never says "simulated" is the UI promising something the system does not do. A guard test pins that disclaimer. 2866 passed, 78 skipped. Verified in a browser against a scratch-DB backend: ?period=live really does come back as period=contest, the preview banner fires, and none of the season chrome renders on the Competition board (checked via getComputedStyle, not element.hidden — a class-level `display` outranks the UA [hidden] rule and the attribute lies about it). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L9cV2UzLgdTVmwcHLAtJeA
|
#335 is merged, so the collision is resolved by merging main in ( Board is now named per @FlyM1ss: Live Trading Leaderboard, opening in Season 0. |
Race.tsx sold "Race on the live leaderboard / Paper trading on live markets. Watch your agent climb against the community" over bullets promising live prices and rankings that update as agents trade. Every clause was false. Board entries come from the curated config/leaderboard.json roster, so no user agent is on any board and no entry path exists; Competition is a fixed historical window, so its prices are not live and its rankings do not move; brokered execution is a PR #328 non-goal with execution/paper_backend.py still a stub. Now names the two boards the app actually serves, and discloses that the Live Trading Leaderboard is in preview for Season 0 -- the landing-side half of the board's own preview banner, since nothing renders that banner one page upstream. The claim survived because its guard was scoped one file too narrowly: test_band_makes_no_paper_trading_claim bans paper[\s-]?trad in WhyCare.tsx only, so it lived on in the neighbouring section. New guards read the shipped bundle, not the TSX, so an unbuilt edit fails rather than passing against stale text. Bundle rebuilt per dashboard/landing/README.md; only the JS and CSS hashes moved (the logo is byte-identical) and the hand-written auth layer is unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L9cV2UzLgdTVmwcHLAtJeA
Pair each board response with its own request (sequence guard + commit the rendered board at the single render entry point), repaint the chrome on the error path and before the fetch, and anchor every preview disclaimer on evidence a season advanced rather than on the period string. Also: drop the unreachable daily_status notice/poll machinery and its three tests, remove the #335 daily->leaderboard redirect that undid the live alias, hide Competition-only chrome on the live board, qualify the home blurb, separate "API unreachable" from "no models ranked yet", take the user agent off the Race illustration, widen the brokered-claim guard to every landing component behind an explicit disclaimer allowlist, give the cache-buster invariant one owner, and default the manual refresh to no model deploys. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Mutation testing found this guard green with the toggle deleted: it checked that the element ids appeared in updateLeaderboardHeader, and a getElementById whose result is discarded satisfies that exactly as well as a working toggle.
The pre-paint added for the cold-start window left the table behind: rerunning the normal renderer over the cleared payload prints "No entries in this season yet", which for the 30-60s of a free-tier cold start is a claim about the board nobody can make, and leaving the old rows re-attributes them to the board being opened. Static placeholder, no interpolation.
Both guards justified themselves with "brokered execution does not exist". It does: execution/robinhood_live_service.py is the live-money path, mounted by api/routers/robinhood_live.py, armed by ROBINHOOD_EXECUTE (default false). The bans are still right — the Lab's Talk/Test/Race flow is simulated throughout and paper_backend.py is a stub — but a guard that states a false reason gets "fixed" by deleting the wrong thing. Hero's conditional sentence is accurate, and is now allowlisted for that reason rather than exempted by scope.
Written 2026-08-09; PRs #352 and #357 shipped a different two-board design six days later. Renames Replay/Forward Season to the shipped Competition/Live Trading, moves the season cadence to two weeks (SEASON_TRADING_DAYS = 10), and marks each Phase 1 task done, superseded or still-valid with verified line numbers. Two premise errors corrected at source: - a custom instruction REPLACES SAFE_TRADING_PROMPT rather than competing with it, so Phase 0's stated reason to expect failure was wrong - #homeGetStartedBtn was already wired on 2026-07-25 (08c85aa) Records three consequences that are not cosmetic: the instruction lock doubles to two weeks and is reopened, C8 now gates Season 1 of an already-shipped board, and Task 7's guard would delete a guard #357 added on purpose. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014K97GJdZ2yxAwQ3mhDaGBG
Renames the Daily Leaderboard tab to the Live Trading Leaderboard and opens it in Season 0.
UI only — there is no season engine yet.
_normalize_periodcoerces an unknown period back tocontest, so?period=livereturns HTTP 200 carrying the Competition board. The tab therefore renders in preview: real curves, full season chrome, and a banner stating that nothing on it is a standing. Detection compares the period requested against the period returned; when the engine ships and answersperiod: "live", the banner clears itself with no frontend change.liveis the canonical tab key.dailyandseasonstay as aliases so deep links already in the wild resolve to the successor board instead of falling through to Competition.Race.tsxcopy corrected: it advertised a live board you could enter your agent in. Entries come from the curateddashboard/config/leaderboard.jsonroster, so no such path exists.No backend source changes. Spec:
docs/superpowers/specs/2026-08-15-live-trading-leaderboard-ui.md