Add Summer 2026 to FutureEval carousel and AIB pages#4859
Conversation
- Add Summer 2026 entry to the FutureEval carousel (now live, demote Spring 2026) - Add Summer 2026 to the AIB tournament pager `TOURNAMENT_ITEMS` - Create `/aib/2026/summer` page with dedicated hero / cards / page-view components - Update `NextTournamentCard` and prior past-tournament pages (Q1/Q2 2025) to link to Summer 2026 - Add `FABSeasonSummer`, `FABStartDateMay4` i18n strings and update `FABTournamentUnderway` and `startCompetingDescription` for Summer 2026 Co-authored-by: Ben Wilson <CodexVeritas@users.noreply.github.com>
📝 WalkthroughWalkthroughThis PR transitions the active AI benchmarking tournament from Spring 2026 to Summer 2026, adding a new tournament route, components, and translations while updating all navigation links. The change is coordinated across six languages and includes server-side bot token resolution for authenticated users. ChangesSummer 2026 Tournament Launch
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
@claude Please Make sure all the languages are properly translated |
|
Claude finished @CodexVeritas's task in 4m 19s —— View job Translation check for Summer 2026 PR
SummaryThe
I followed the existing house style for each locale (e.g. Czech declension Notes / follow-ups
|
Adds FABStartDateMay4 and FABSeasonSummer to each non-English locale, and updates FABTournamentUnderway + startCompetingDescription from "Spring 2026" to "Summer 2026". Co-authored-by: Ben Wilson <CodexVeritas@users.noreply.github.com>
🚀 Preview EnvironmentYour preview environment is ready!
Details
ℹ️ Preview Environment InfoIsolation:
Limitations:
Cleanup:
|
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (3)
front_end/src/app/(main)/aib/components/tournament-pager.tsx (1)
173-181: 🏗️ Heavy liftRefactor tournament item titles to i18n-backed values before adding more seasons.
The new entry continues a static-English pattern, so localized users still see English season labels in the pager. Consider replacing the exported static list with a translation-backed factory used inside the client component.
Based on learnings: Do not hardcode English strings in TSX components; prefer
useTranslations()and i18n keys.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@front_end/src/app/`(main)/aib/components/tournament-pager.tsx around lines 173 - 181, TOURNAMENT_ITEMS is currently a static array of English titles; replace it with a factory that returns translated titles using the client-side i18n hook (e.g., useTranslations) instead of exporting static strings. Change the export to a function (e.g., getTournamentItems or buildTournamentItems) that calls useTranslations() inside the component (or is invoked from the component) to produce items with localized title values while keeping the href values the same; update components that import TOURNAMENT_ITEMS to call the new factory from client code so titles render localized labels.Source: Learnings
front_end/src/app/(main)/aib/components/cards-2026-summer.tsx (2)
47-49: 💤 Low valueUnused conditional: "Coming Soon" check appears to be dead code.
The condition
heading2 === "Coming Soon"on line 47 will never match because the actual values passed are"4 de mayo"(from translation) and"$58,000". This appears to be leftover code from a template.♻️ Suggested cleanup
If the "Coming Soon" condition is not needed, simplify to a single responsive class:
<div className={cn( - heading2 === "Coming Soon" - ? "text-xl md:text-3xl lg:text-4xl min-[1920px]:text-5xl" - : "text-2xl md:text-4xl lg:text-6xl min-[1920px]:text-7xl" + "text-2xl md:text-4xl lg:text-6xl min-[1920px]:text-7xl" )} >Or retain the flexibility if future cards may use "Coming Soon".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@front_end/src/app/`(main)/aib/components/cards-2026-summer.tsx around lines 47 - 49, The ternary checking heading2 === "Coming Soon" is dead code; remove the conditional and replace the ternary expression with a single responsive class string used for the element’s className (the JSX that currently computes classes based on heading2). Locate the JSX where heading2 is used (variable heading2 in the cards-2026-summer component) and update the class expression to a single constant like "text-2xl md:text-4xl lg:text-6xl min-[1920px]:text-7xl" (or choose the other size if preferred), or keep the conditional but parameterize the match to a prop if you intend to support "Coming Soon" in future.
24-24: ⚡ Quick winConsider using a translation key for the prize value.
The prize amount
"$58,000"is hardcoded, while other text uses translation keys. This could cause inconsistency if currency formatting varies by locale or if the prize value changes.♻️ Suggested approach
Define a translation key like
FABPrizeValueSummer2026in all language files, then reference it:function PrizeSummer2026() { const t = useTranslations(); return ( <Card className="rounded-md border-olive-700 bg-olive-500/50 text-olive-800 dark:bg-olive-500/30 dark:text-olive-900-dark" heading1={t("FABPrizePool")} - heading2={"$58,000"} + heading2={t("FABPrizeValueSummer2026")} /> ); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@front_end/src/app/`(main)/aib/components/cards-2026-summer.tsx at line 24, Replace the hardcoded prize string passed to the heading2 prop in the cards-2026-summer.tsx component with a translation lookup (e.g., use t('FABPrizeValueSummer2026')) and add the corresponding FABPrizeValueSummer2026 key to all locale files; ensure the translation value contains the properly formatted currency (or use a localized number/currency formatter in the component if you prefer dynamic formatting) so the heading2 prop receives the translated/formatted prize instead of "$58,000".
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@front_end/messages/cs.json`:
- Line 2021: The Czech string for startCompetingDescription ends with
"dvoutýdenního" and relies on a separate miniBench key; fix by making
startCompetingDescription self-contained and grammatically correct: replace the
trailing "dvoutýdenního" with "dvoutýdenního MiniBench" (ensuring a single space
before "MiniBench" and correct capitalization), or alternatively keep
startCompetingDescription complete and stop concatenating with the
"miniBench"/"MiniBench" key; update the keys startCompetingDescription and/or
miniBench accordingly so the UI shows "… nebo se zúčastněte dvoutýdenního
MiniBench" as a complete phrase.
In `@front_end/messages/en.json`:
- Line 2078: The UI shows two different Summer 2026 prize amounts; make them
consistent by picking the correct prize value and updating all references:
either change the translation key startCompetingDescription to match the
hardcoded heading2 in the cards-2026-summer.tsx component, or change heading2 to
match the translation (and update the FABPrizeValue constant if that is meant to
be the canonical value); ensure startCompetingDescription, the heading2 prop in
the cards-2026-summer component, and FABPrizeValue all use the same dollar
amount string.
In `@front_end/src/app/`(main)/aib/2026/summer/page.tsx:
- Around line 12-35: Wrap the network calls inside getPrimaryBotToken in a
try/catch so any thrown error from ServerProfileApi.getMyProfile,
ServerAuthApi.getApiKey, ServerProfileApi.getMyBots, or
ServerProfileApi.getBotToken is caught and the function returns null instead of
letting the error bubble up; keep existing logic (check user.is_bot, find
primary bot, return token) inside the try block and in catch log the error
(optional) and return null as a graceful fallback.
In `@front_end/src/app/`(main)/aib/components/page-view-2026-summer.tsx:
- Around line 176-182: The Link elements in page-view-2026-summer.tsx that use
target="_blank" (the JSX fragments rendering terms and privacy links) are
missing rel attributes; update those Link usages to include rel="noopener
noreferrer" alongside target="_blank" to harden external links opened in a new
tab (locate the Link instances inside the terms and privacy render
functions/components and add the rel attribute).
- Around line 144-156: Replace the hardcoded CTA labels inside the JSX (the
"Summer 2026 Bot Tournament" and "MiniBench Tournaments" text nodes) with
localized strings using useTranslations(); e.g., import and call const t =
useTranslations('<appropriateNamespace>') at the top of the
page-view-2026-summer component and replace the span contents with
t('summer2026BotTournament') and t('miniBenchTournaments'); then add those keys
to the locale dictionaries so translations are available. Ensure you use the
same namespace as other AIB strings to keep consistency.
---
Nitpick comments:
In `@front_end/src/app/`(main)/aib/components/cards-2026-summer.tsx:
- Around line 47-49: The ternary checking heading2 === "Coming Soon" is dead
code; remove the conditional and replace the ternary expression with a single
responsive class string used for the element’s className (the JSX that currently
computes classes based on heading2). Locate the JSX where heading2 is used
(variable heading2 in the cards-2026-summer component) and update the class
expression to a single constant like "text-2xl md:text-4xl lg:text-6xl
min-[1920px]:text-7xl" (or choose the other size if preferred), or keep the
conditional but parameterize the match to a prop if you intend to support
"Coming Soon" in future.
- Line 24: Replace the hardcoded prize string passed to the heading2 prop in the
cards-2026-summer.tsx component with a translation lookup (e.g., use
t('FABPrizeValueSummer2026')) and add the corresponding FABPrizeValueSummer2026
key to all locale files; ensure the translation value contains the properly
formatted currency (or use a localized number/currency formatter in the
component if you prefer dynamic formatting) so the heading2 prop receives the
translated/formatted prize instead of "$58,000".
In `@front_end/src/app/`(main)/aib/components/tournament-pager.tsx:
- Around line 173-181: TOURNAMENT_ITEMS is currently a static array of English
titles; replace it with a factory that returns translated titles using the
client-side i18n hook (e.g., useTranslations) instead of exporting static
strings. Change the export to a function (e.g., getTournamentItems or
buildTournamentItems) that calls useTranslations() inside the component (or is
invoked from the component) to produce items with localized title values while
keeping the href values the same; update components that import TOURNAMENT_ITEMS
to call the new factory from client code so titles render localized labels.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 616151b8-66be-4d59-867c-21cde5c3e415
📒 Files selected for processing (15)
front_end/messages/cs.jsonfront_end/messages/en.jsonfront_end/messages/es.jsonfront_end/messages/pt.jsonfront_end/messages/zh-TW.jsonfront_end/messages/zh.jsonfront_end/src/app/(futureeval)/futureeval/components/futureeval-tournaments.tsxfront_end/src/app/(main)/aib/2026/summer/page.tsxfront_end/src/app/(main)/aib/components/cards-2026-summer.tsxfront_end/src/app/(main)/aib/components/hero-2026-summer.tsxfront_end/src/app/(main)/aib/components/next-tournament-card.tsxfront_end/src/app/(main)/aib/components/page-view-2025-q1.tsxfront_end/src/app/(main)/aib/components/page-view-2025-q2.tsxfront_end/src/app/(main)/aib/components/page-view-2026-summer.tsxfront_end/src/app/(main)/aib/components/tournament-pager.tsx
| "botsVsHumansDescription": "Pořádáme sezónní a dvoutýdenní turnaje botů, otevřené pro všechny tvůrce. Boti soutěží proti sobě navzájem a jsou porovnáváni s nejlepšími lidskými předpovídači.", | ||
| "startCompeting": "Začněte soutěžit", | ||
| "startCompetingDescription": "Připojte se k více než 100 týmům a jednotlivým tvůrcům botů, kteří soutěží o cenový fond ve výši 50 000 dolarů na jaře 2026 nebo se zúčastněte dvoutýdenního", | ||
| "startCompetingDescription": "Připojte se k více než 100 týmům a jednotlivým tvůrcům botů, kteří soutěží o cenový fond ve výši 50 000 dolarů v létě 2026 nebo se zúčastněte dvoutýdenního", |
There was a problem hiding this comment.
❓ Verification inconclusive
🏁 Script executed:
#!/bin/bash
# Check how other languages handle the startCompetingDescription ending
rg -A 1 '"startCompetingDescription"' front_end/messages/Repository: Metaculus/metaculus
Length of output: 1429
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect cs.json around the cited line and find miniBench value
nl -ba front_end/messages/cs.json | sed -n '2000,2050p'
# Find where startCompetingDescription is used and how miniBench is appended
rg -n --hidden --glob '!**/node_modules/**' 'startCompetingDescription' front_end | head -n 50
# Find usage of miniBench in front_end
rg -n --hidden --glob '!**/node_modules/**' 'miniBench' front_end | head -n 50
# Also inspect en.json around the same key to see if UI expects concatenation
nl -ba front_end/messages/en.json | sed -n '2000,2050p'Repository: Metaculus/metaculus
Length of output: 106
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect cs.json around the cited line and find miniBench value
nl -ba front_end/messages/cs.json | sed -n '2000,2050p'
# Find where startCompetingDescription is used and how miniBench is appended
rg -n --hidden --glob '!**/node_modules/**' 'startCompetingDescription' front_end | head -n 50
# Find usage of miniBench in front_end
rg -n --hidden --glob '!**/node_modules/**' 'miniBench' front_end | head -n 50
# Also inspect en.json around the same key to see if UI expects concatenation
nl -ba front_end/messages/en.json | sed -n '2000,2050p'Repository: Metaculus/metaculus
Length of output: 106
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Show cs.json around the relevant line (use sed without nl)
sed -n '2010,2035p' front_end/messages/cs.json
# Show en.json around the relevant key (same range)
sed -n '2010,2035p' front_end/messages/en.json
# Find where startCompetingDescription is referenced in the front_end code
rg -n --hidden --glob '!**/node_modules/**' --glob '!**/dist/**' 'startCompetingDescription' front_end | head -n 50
# Find where miniBench is referenced/concatenated
rg -n --hidden --glob '!**/node_modules/**' --glob '!**/dist/**' 'miniBench' front_end | head -n 80Repository: Metaculus/metaculus
Length of output: 5273
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Find code usage of the i18n keys (excluding message JSON files)
rg -n --glob '!front_end/messages/*.json' --glob '!front_end/messages/*' 'startCompetingDescription' front_end | head -n 50
rg -n --glob '!front_end/messages/*.json' --glob '!front_end/messages/*' 'miniBench' front_end | head -n 50
# Also search for string concatenation patterns involving the keys (if any)
rg -n --glob '!front_end/messages/*.json' --glob '!front_end/messages/*' 'startCompetingDescription.*miniBench|miniBench.*startCompetingDescription' front_end | head -n 50Repository: Metaculus/metaculus
Length of output: 45
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Broader searches: exclude only the JSON message files, search everything else in front_end
rg -n --hidden --glob '!front_end/messages/*.json' 'startCompetingDescription' front_end | head -n 80
rg -n --hidden --glob '!front_end/messages/*.json' 'miniBench' front_end | head -n 80
# Search for rendering of the "miniBench" label near other localized strings
rg -n --hidden --glob '!front_end/messages/*.json' 'dvoutýdenn|dvoutýden' front_end | head -n 50Repository: Metaculus/metaculus
Length of output: 45
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Search for other related keys outside the JSON message files
rg -n --hidden --glob '!front_end/messages/*.json' 'startCompeting' front_end | head -n 80
# Search for miniBench usage outside the JSON message files
rg -n --hidden --glob '!front_end/messages/*.json' 'MiniBench' front_end | head -n 80
rg -n --hidden --glob '!front_end/messages/*.json' '"miniBench"' front_end | head -n 80
rg -n --hidden --glob '!front_end/messages/*.json' 'miniBench' front_end | head -n 80
# Find the component/page that renders the "startCompetingDescription" content
rg -n --hidden --glob '!front_end/messages/*.json' 'startCompetingDescription' front_end | head -n 80
rg -n --hidden --glob '!front_end/messages/*.json' 'startCompetingDescription' . | head -n 80Repository: Metaculus/metaculus
Length of output: 45
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Search entire repo for exact key strings (excluding common generated dirs)
rg -n --hidden --glob '!**/node_modules/**' --glob '!**/dist/**' --glob '!**/build/**' --glob '!**/.next/**' --glob '!**/coverage/**' '"startCompetingDescription"' . | head -n 50
rg -n --hidden --glob '!**/node_modules/**' --glob '!**/dist/**' --glob '!**/build/**' --glob '!**/.next/**' --glob '!**/coverage/**' '"miniBench"' . | head -n 50
# Also search for the end fragment shown in Czech to locate where it's used
rg -n --hidden --glob '!**/node_modules/**' --glob '!**/dist/**' --glob '!**/build/**' --glob '!**/.next/**' 'dvoutýdenn' front_end . | head -n 50Repository: Metaculus/metaculus
Length of output: 4434
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Search for components/strings referencing the feature (exclude message bundles)
rg -n --hidden --glob '!front_end/messages/**' --glob '!**/node_modules/**' 'startCompeting' front_end | head -n 80
rg -n --hidden --glob '!front_end/messages/**' --glob '!**/node_modules/**' 'miniBench' front_end | head -n 80
rg -n --hidden --glob '!front_end/messages/**' --glob '!**/node_modules/**' 'minibench' front_end | head -n 80
# Search for rendering of the "dvoutýdenního" fragment (should only appear in cs.json if not concatenated in code)
rg -n --hidden --glob '!front_end/messages/cs.json' --glob '!**/node_modules/**' 'dvoutýdenního' . | head -n 20Repository: Metaculus/metaculus
Length of output: 45
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect nearby Czech strings around minibench keys
rg -n --hidden 'minibench|MiniBench|dvoutýdenn' front_end/messages/cs.json
# Check whether any Czech string already includes "dvoutýdenního MiniBench"
rg -n --hidden 'dvoutýdenn.*MiniBench|MiniBench.*dvoutýdenn' front_end/messages/cs.jsonRepository: Metaculus/metaculus
Length of output: 2452
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate where the "MiniBench" label is rendered in the UI (outside message bundles)
rg -n --hidden --glob '!front_end/messages/*.json' --glob '!**/node_modules/**' 'MiniBench' front_end | head -n 80
# Locate components related to "tournaments" / "startCompeting" / "competing" in non-message code
rg -n --hidden --glob '!front_end/messages/*.json' --glob '!**/node_modules/**' 'startCompeting|startCompetingDescription|competing for|biweekly' front_end | head -n 80
# Find the i18n loader usage for front_end/messages
rg -n --hidden --glob '!**/node_modules/**' '(messages/cs\.json|front_end/messages|loadMessages|i18n|next-intl|react-intl|useTranslation|t\()' front_end | head -n 80Repository: Metaculus/metaculus
Length of output: 9674
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect translations type to see how message keys are referenced in code
sed -n '1,200p' front_end/src/types/translations.ts
# Search for the page/component that likely renders the "startCompeting" copy around MiniBench
rg -n --hidden --glob '!front_end/messages/*.json' 'aibMiniBench|MiniBench|startCompeting' front_end/src/app front_end/src/components front_end/src | head -n 120
# Specifically inspect the likely AIB page(s) that might use startCompetingDescription
rg -n --hidden --glob '!front_end/messages/*.json' 't\(([^)]*startCompeting|startCompeting)' front_end/src | head -n 80
rg -n --hidden --glob '!front_end/messages/*.json' 'startCompetingDescription' front_end/src | head -n 80
rg -n --hidden --glob '!front_end/messages/*.json' 'miniBench' front_end/src | head -n 80Repository: Metaculus/metaculus
Length of output: 2668
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the relevant AIB page view components around the reported lines
sed -n '120,220p' front_end/src/app/(main)/aib/components/page-view.tsx
sed -n '120,240p' front_end/src/app/(main)/aib/components/page-view-2026-summer.tsx
# Search for the translation keys as literals in these files
rg -n "startCompetingDescription|miniBench|startCompeting" front_end/src/app/(main)/aib/components/page-view.tsx
rg -n "startCompetingDescription|miniBench|startCompeting" front_end/src/app/(main)/aib/components/page-view-2026-summer.tsx
# Broader: search AIB pages/components for how the start section is rendered
rg -n "startCompetingDescription|startCompeting|miniBench" front_end/src/app/(main)/aib | head -n 80
rg -n "startCompetingDescription|startCompeting|miniBench" front_end/src/app | head -n 80Repository: Metaculus/metaculus
Length of output: 125
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Quote paths containing parentheses
sed -n '120,220p' "front_end/src/app/(main)/aib/components/page-view.tsx"
sed -n '120,240p' "front_end/src/app/(main)/aib/components/page-view-2026-summer.tsx"
rg -n "startCompetingDescription|miniBench|startCompeting" "front_end/src/app/(main)/aib/components/page-view.tsx"
rg -n "startCompetingDescription|miniBench|startCompeting" "front_end/src/app/(main)/aib/components/page-view-2026-summer.tsx"
rg -n "startCompetingDescription|startCompeting|miniBench" "front_end/src/app/(main)/aib" | head -n 120Repository: Metaculus/metaculus
Length of output: 8236
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Search for the exact keys in the AIB tournament components (quoted paths for parentheses)
rg -n "startCompetingDescription" "front_end/src/app/(main)/aib/components/page-view.tsx" "front_end/src/app/(main)/aib/components/page-view-2026-summer.tsx" || true
# Search the whole AIB components directory for those keys
rg -n "startCompetingDescription|miniBench\"|t\\(\"miniBench\"\\)|t\\.rich\\(\"miniBench\"|t\\(\"startCompeting\"\\)|t\\(\"startCompetingDescription\"\\)" "front_end/src/app/(main)/aib/components" || true
# If startCompetingDescription isn't referenced directly, look for "dvoutýdenního" fragment or "biweekly"/"MiniBench" concatenation logic
rg -n "dvoutýdenn|biweekly|bi-weekly|MiniBench|minibench" "front_end/src/app/(main)/aib/components" || true
# Show a larger slice around the start of the AiBenchmarkingTournamentPage components (may include where the description is rendered)
sed -n '1,220p' "front_end/src/app/(main)/aib/components/page-view.tsx"Repository: Metaculus/metaculus
Length of output: 10109
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate where startCompetingDescription is used within the AIB components directory
rg -n "startCompetingDescription|startCompeting" "front_end/src/app/(main)/aib/components" || true
rg -n "startCompetingDescription|startCompeting" "front_end/src/app/(main)/aib" || true
# Inspect likely component that renders the start/competing description
ls -la "front_end/src/app/(main)/aib/components"
sed -n '1,220p' "front_end/src/app/(main)/aib/components/description.tsx" || true
# Also inspect other closely related components if present
for f in "hero.tsx" "cards.tsx" "tournament-pager.tsx" ; do
if [ -f "front_end/src/app/(main)/aib/components/$f" ]; then
echo "---- $f ----"
sed -n '1,220p' "front_end/src/app/(main)/aib/components/$f"
fi
doneRepository: Metaculus/metaculus
Length of output: 12131
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Search non-message code for the key usage (should be near the place where Czech grammar matters)
rg -n --hidden --glob '!front_end/messages/*.json' 'startCompetingDescription' front_end/src | head -n 50
# If not referenced as a string, check for i18n type-safe usage patterns using the key name
rg -n --hidden --glob '!front_end/messages/*.json' 'startCompeting' front_end/src | head -n 100
# Search for where the "startCompeting" section might be rendered (tournament overview pages)
rg -n --hidden --glob '!front_end/messages/*.json' 'FABStart|FABCreate|GettingStarted|FABdesc' front_end/src/app/(main)/aib | head -n 120
# Inspect likely page(s) if the key is used in AIB 2026 spring/summer or related sections
rg -n --hidden --glob '!front_end/messages/*.json' 'FAB' front_end/src/app/(main)/aib | head -n 120Repository: Metaculus/metaculus
Length of output: 45
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Search for substrings that might locate dynamic usage (excluding message bundles)
rg -n --hidden --glob '!front_end/messages/*.json' 'CompetingDescription' . | head -n 80
rg -n --hidden --glob '!front_end/messages/*.json' 'startCompeting' . | head -n 80
# Search for "biweekly" usage in code (to find where the English string is assembled)
rg -n --hidden --glob '!front_end/messages/*.json' 'biweekly' . | head -n 80
# Search for "zúčastněte se" usage or Czech-specific fragment
rg -n --hidden --glob '!front_end/messages/*.json' 'zúčastněte se' . | head -n 20
# Search for "miniBench" usage in non-message code (not just in messages)
rg -n --hidden --glob '!front_end/messages/*.json' 'miniBench' front_end/src | head -n 80Repository: Metaculus/metaculus
Length of output: 45
Fix Czech string completeness/case for startCompetingDescription + miniBench.
front_end/messages/cs.json ends startCompetingDescription with "dvoutýdenního" while miniBench is a separate message key ("MiniBench"). If the UI concatenates these two pieces, the intended result should read "… nebo se zúčastněte dvoutýdenního MiniBench" (genitive after zúčastněte se). If the UI does not append miniBench (or appends with wrong spacing/case), this produces an incomplete sentence fragment and should be updated to include the noun in the same string (or adjust accordingly).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@front_end/messages/cs.json` at line 2021, The Czech string for
startCompetingDescription ends with "dvoutýdenního" and relies on a separate
miniBench key; fix by making startCompetingDescription self-contained and
grammatically correct: replace the trailing "dvoutýdenního" with "dvoutýdenního
MiniBench" (ensuring a single space before "MiniBench" and correct
capitalization), or alternatively keep startCompetingDescription complete and
stop concatenating with the "miniBench"/"MiniBench" key; update the keys
startCompetingDescription and/or miniBench accordingly so the UI shows "… nebo
se zúčastněte dvoutýdenního MiniBench" as a complete phrase.
| "botsVsHumansDescription": "We run seasonal and biweekly bot tournaments, open to all builders. Bots compete against each other and are benchmarked against top human forecasters.", | ||
| "startCompeting": "Start competing", | ||
| "startCompetingDescription": "Join 100+ teams and individual bot builders competing for a $50,000 prize pool in Spring 2026 or enter the biweekly", | ||
| "startCompetingDescription": "Join 100+ teams and individual bot builders competing for a $50,000 prize pool in Summer 2026 or enter the biweekly", |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Check for other prize pool references to understand context
echo "=== Searching for prize pool references in translation files ==="
rg -n "50,000|50000|\$50" front_end/messages/
echo -e "\n=== Searching for FABPrizeValue usage in codebase ==="
rg -n "FABPrizeValue" --type=ts --type=tsx
echo -e "\n=== Searching for startCompetingDescription usage ==="
rg -n "startCompetingDescription" --type=ts --type=tsxRepository: Metaculus/metaculus
Length of output: 1143
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== FABPrizeValue references (translations + repo) ==="
rg -n "FABPrizeValue" front_end/messages/ || true
rg -n "FABPrizeValue" . || true
echo -e "\n=== startCompetingDescription references (translations + repo) ==="
rg -n "startCompetingDescription" front_end/messages/ || true
rg -n "startCompetingDescription" . || true
echo -e "\n=== Prize pool references near other tournament keys in en.json ==="
# Show the nearby context around the en.json occurrences of 50,000 / 30,000 if present
rg -n "50,000|\$50|30,000|\$30" front_end/messages/en.json || trueRepository: Metaculus/metaculus
Length of output: 3693
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== startCompetingDescription usage in front_end/src ==="
rg -n "t\\(\"startCompetingDescription\"\\)|startCompetingDescription" front_end/src --glob='*.ts' --glob='*.tsx' || true
echo -e "\n=== FABPrizeValue usage in front_end/src (components) ==="
sed -n '1,120p' front_end/src/app/(main)/aib/components/cards-q1.tsx
sed -n '1,120p' front_end/src/app/(main)/aib/components/cards-q2.tsx
sed -n '1,120p' front_end/src/app/(main)/aib/components/cards-q3.tsx
sed -n '1,120p' front_end/src/app/(main)/aib/components/cards-q4.tsxRepository: Metaculus/metaculus
Length of output: 259
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== startCompetingDescription usage in front_end/src ==="
rg -n "startCompetingDescription" front_end/src --glob='*.ts' --glob='*.tsx' || true
echo -e "\n=== FABPrizeValue usage in front_end/src ==="
rg -n "FABPrizeValue" front_end/src --glob='*.ts' --glob='*.tsx' || true
echo -e "\n=== Locate cards-q*.tsx ==="
fd -a "cards-q*.tsx" front_end/src || true
echo -e "\n=== Show top + FABPrizeValue usage context in cards-q1..q4 ==="
for f in $(fd -a "cards-q[1-4].tsx" front_end/src | sort); do
echo -e "\n--- $f ---"
# print first 120 lines and any line containing FABPrizeValue
nl -ba "$f" | head -n 140 | sed -n '1,140p'
rg -n "FABPrizeValue" "$f" || true
doneRepository: Metaculus/metaculus
Length of output: 770
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== cards-q*.tsx files ==="
fd -a "cards-q[1-4].tsx" front_end/src | sort
for f in $(fd -a "cards-q[1-4].tsx" front_end/src | sort); do
echo -e "\n--- $f (first 120 lines) ---"
sed -n '1,120p' "$f"
echo -e "\n--- $f (lines with FABPrizeValue + context) ---"
rg -n "FABPrizeValue" "$f" || true
# Show a small window around the occurrences
# (get line numbers from rg output)
while read -r line; do
ln=$(echo "$line" | cut -d: -f2)
start=$((ln-8))
end=$((ln+12))
echo -e "\nWindow around line $ln:"
sed -n "${start},${end}p" "$f"
done < <(rg -n "FABPrizeValue" "$f" || true)
doneRepository: Metaculus/metaculus
Length of output: 2292
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== en.json around FABPrizePool / FABPrizeValue ==="
sed -n '1085,1135p' front_end/messages/en.json
echo -e "\n=== en.json around startCompetingDescription ==="
sed -n '2055,2105p' front_end/messages/en.json
echo -e "\n=== startCompetingDescription usage in front_end/src (ts/tsx) ==="
rg -n "startCompetingDescription" front_end/src --glob='*.ts' --glob='*.tsx' || true
echo -e "\n=== FABPrizePool usage in front_end/src (ts/tsx) ==="
rg -n "FABPrizePool" front_end/src --glob='*.ts' --glob='*.tsx' || true
echo -e "\n=== Summer 2026 references in front_end/src ==="
rg -n "Summer 2026" front_end/src --glob='*.ts' --glob='*.tsx' || trueRepository: Metaculus/metaculus
Length of output: 8466
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== startCompetingDescription key usage across front_end ==="
rg -n "startCompetingDescription" front_end --glob='*.ts' --glob='*.tsx' --glob='*.js' --glob='*.jsx' --glob='*.md' --glob='*.json' --glob='*.html' || true
echo -e "\n=== startCompeting key usage ==="
rg -n "t\\(\"startCompetingDescription\"\\)|t\\(\"startCompeting\"\\)" front_end --glob='*.ts' --glob='*.tsx' --glob='*.js' --glob='*.jsx' || true
echo -e "\n=== Inspect cards-2026-summer.tsx ==="
sed -n '1,200p' front_end/src/app/(main)/aib/components/cards-2026-summer.tsx
echo -e "\n=== Inspect tournament-pager.tsx around Summer 2026 ==="
sed -n '140,230p' front_end/src/app/(main)/aib/components/tournament-pager.tsx
echo -e "\n=== Inspect page-view-2026-summer.tsx around where cards are rendered ==="
# show around mid file
sed -n '110,210p' front_end/src/app/(main)/aib/components/page-view-2026-summer.tsxRepository: Metaculus/metaculus
Length of output: 1331
🏁 Script executed:
#!/bin/bash
set -euo pipefail
cards_base="front_end/src/app/(main)/aib/components"
echo "=== Inspect cards-2026-summer.tsx ==="
sed -n '1,220p' "${cards_base}/cards-2026-summer.tsx"
echo -e "\n=== Inspect tournament-pager.tsx around Summer 2026 ==="
sed -n '150,230p' "front_end/src/app/(main)/aib/components/tournament-pager.tsx"
echo -e "\n=== Inspect page-view-2026-summer.tsx around card rendering ==="
sed -n '120,240p' "front_end/src/app/(main)/aib/components/page-view-2026-summer.tsx"Repository: Metaculus/metaculus
Length of output: 6832
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n "startCompetingDescription" . || trueRepository: Metaculus/metaculus
Length of output: 1128
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Search for $58,000 / 58000 ==="
rg -n "\\$58,000|58000" . || true
echo -e "\n=== Search for $50,000 / 50000 ==="
rg -n "\\$50,000|50000" front_end/src front_end/messages . || true
echo -e "\n=== Search for Summer 2026 prize pool mentions ==="
rg -n "Summer 2026|2026/summer|2026-summer" front_end/src --glob='*.ts' --glob='*.tsx' || trueRepository: Metaculus/metaculus
Length of output: 103
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Search for \$58,000 ==="
rg -n '\$58,000' . || true
echo -e "\n=== Search for \$50,000 ==="
rg -n '\$50,000|50000' front_end/src front_end/messages . || true
echo -e "\n=== Search for Summer 2026 prize pool mentions ==="
rg -n 'Summer 2026|/aib/2026/summer|2026/summer' front_end/src --glob='*.ts' --glob='*.tsx' --glob='*.js' --glob='*.jsx' || trueRepository: Metaculus/metaculus
Length of output: 50377
Fix inconsistent Summer 2026 prize pool text
front_end/messages/en.jsonstartCompetingDescriptionsays “$50,000 prize pool in Summer 2026”.- The Summer 2026 prize card hardcodes
heading2={"$58,000"}infront_end/src/app/(main)/aib/components/cards-2026-summer.tsx. FABPrizeValue($30,000) is used for the Q1–Q4 cards, so it doesn’t resolve the Summer 2026 mismatch.
Update whichever is wrong so the Summer 2026 prize amount is consistent across the UI and translation.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@front_end/messages/en.json` at line 2078, The UI shows two different Summer
2026 prize amounts; make them consistent by picking the correct prize value and
updating all references: either change the translation key
startCompetingDescription to match the hardcoded heading2 in the
cards-2026-summer.tsx component, or change heading2 to match the translation
(and update the FABPrizeValue constant if that is meant to be the canonical
value); ensure startCompetingDescription, the heading2 prop in the
cards-2026-summer component, and FABPrizeValue all use the same dollar amount
string.
| async function getPrimaryBotToken() { | ||
| const user = await ServerProfileApi.getMyProfile(); | ||
|
|
||
| if (!user) { | ||
| return null; | ||
| } | ||
|
|
||
| if (user.is_bot) { | ||
| const { key } = await ServerAuthApi.getApiKey(); | ||
| return key; | ||
| } | ||
|
|
||
| const bots = await ServerProfileApi.getMyBots(); | ||
| const primaryBot = bots.find((bot) => bot.is_primary_bot); | ||
|
|
||
| if (primaryBot) { | ||
| const { token: botToken } = await ServerProfileApi.getBotToken( | ||
| primaryBot.id | ||
| ); | ||
| return botToken; | ||
| } | ||
|
|
||
| return null; | ||
| } |
There was a problem hiding this comment.
Handle token lookup failures with a graceful fallback.
A thrown error from any server call here can take down the entire page render. This path should degrade to null so users can still access the page when bot/token endpoints fail transiently.
Proposed fix
async function getPrimaryBotToken() {
- const user = await ServerProfileApi.getMyProfile();
+ try {
+ const user = await ServerProfileApi.getMyProfile();
- if (!user) {
- return null;
- }
+ if (!user) {
+ return null;
+ }
- if (user.is_bot) {
- const { key } = await ServerAuthApi.getApiKey();
- return key;
- }
+ if (user.is_bot) {
+ const { key } = await ServerAuthApi.getApiKey();
+ return key;
+ }
- const bots = await ServerProfileApi.getMyBots();
- const primaryBot = bots.find((bot) => bot.is_primary_bot);
+ const bots = await ServerProfileApi.getMyBots();
+ const primaryBot = bots.find((bot) => bot.is_primary_bot);
- if (primaryBot) {
- const { token: botToken } = await ServerProfileApi.getBotToken(
- primaryBot.id
- );
- return botToken;
- }
+ if (primaryBot) {
+ const { token: botToken } = await ServerProfileApi.getBotToken(
+ primaryBot.id
+ );
+ return botToken;
+ }
- return null;
+ return null;
+ } catch {
+ return null;
+ }
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| async function getPrimaryBotToken() { | |
| const user = await ServerProfileApi.getMyProfile(); | |
| if (!user) { | |
| return null; | |
| } | |
| if (user.is_bot) { | |
| const { key } = await ServerAuthApi.getApiKey(); | |
| return key; | |
| } | |
| const bots = await ServerProfileApi.getMyBots(); | |
| const primaryBot = bots.find((bot) => bot.is_primary_bot); | |
| if (primaryBot) { | |
| const { token: botToken } = await ServerProfileApi.getBotToken( | |
| primaryBot.id | |
| ); | |
| return botToken; | |
| } | |
| return null; | |
| } | |
| async function getPrimaryBotToken() { | |
| try { | |
| const user = await ServerProfileApi.getMyProfile(); | |
| if (!user) { | |
| return null; | |
| } | |
| if (user.is_bot) { | |
| const { key } = await ServerAuthApi.getApiKey(); | |
| return key; | |
| } | |
| const bots = await ServerProfileApi.getMyBots(); | |
| const primaryBot = bots.find((bot) => bot.is_primary_bot); | |
| if (primaryBot) { | |
| const { token: botToken } = await ServerProfileApi.getBotToken( | |
| primaryBot.id | |
| ); | |
| return botToken; | |
| } | |
| return null; | |
| } catch { | |
| return null; | |
| } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@front_end/src/app/`(main)/aib/2026/summer/page.tsx around lines 12 - 35, Wrap
the network calls inside getPrimaryBotToken in a try/catch so any thrown error
from ServerProfileApi.getMyProfile, ServerAuthApi.getApiKey,
ServerProfileApi.getMyBots, or ServerProfileApi.getBotToken is caught and the
function returns null instead of letting the error bubble up; keep existing
logic (check user.is_bot, find primary bot, return token) inside the try block
and in catch log the error (optional) and return null as a graceful fallback.
| Summer 2026 Bot Tournament | ||
| </span> | ||
| </Link> | ||
| <Link | ||
| href="/aib/minibench" | ||
| className="flex w-full items-center gap-4 rounded bg-white p-4 no-underline transition-all hover:bg-blue-500/40 dark:bg-blue-100-dark dark:hover:bg-blue-600/40 md:p-5 min-[1920px]:p-6" | ||
| > | ||
| <FontAwesomeIcon | ||
| icon={faChartBar} | ||
| className="text-2xl text-blue-700 dark:text-blue-700-dark md:text-3xl min-[1920px]:text-4xl" | ||
| /> | ||
| <span className="text-lg font-medium md:text-xl min-[1920px]:text-2xl"> | ||
| MiniBench Tournaments |
There was a problem hiding this comment.
Localize the newly added CTA labels instead of hardcoding English.
These strings bypass locale dictionaries and will remain English in translated UIs.
Proposed fix
- <span className="text-lg font-medium md:text-xl min-[1920px]:text-2xl">
- Summer 2026 Bot Tournament
- </span>
+ <span className="text-lg font-medium md:text-xl min-[1920px]:text-2xl">
+ {`${t("FABSeasonSummer")} 2026 ${t("Tournament")}`}
+ </span>
...
- <span className="text-lg font-medium md:text-xl min-[1920px]:text-2xl">
- MiniBench Tournaments
- </span>
+ <span className="text-lg font-medium md:text-xl min-[1920px]:text-2xl">
+ {`${t("miniBench")} ${t("tournaments")}`}
+ </span>Based on learnings: Do not hardcode English strings in TSX components; prefer useTranslations() and i18n keys.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| Summer 2026 Bot Tournament | |
| </span> | |
| </Link> | |
| <Link | |
| href="/aib/minibench" | |
| className="flex w-full items-center gap-4 rounded bg-white p-4 no-underline transition-all hover:bg-blue-500/40 dark:bg-blue-100-dark dark:hover:bg-blue-600/40 md:p-5 min-[1920px]:p-6" | |
| > | |
| <FontAwesomeIcon | |
| icon={faChartBar} | |
| className="text-2xl text-blue-700 dark:text-blue-700-dark md:text-3xl min-[1920px]:text-4xl" | |
| /> | |
| <span className="text-lg font-medium md:text-xl min-[1920px]:text-2xl"> | |
| MiniBench Tournaments | |
| {`${t("FABSeasonSummer")} 2026 ${t("Tournament")}`} | |
| </span> | |
| </Link> | |
| <Link | |
| href="/aib/minibench" | |
| className="flex w-full items-center gap-4 rounded bg-white p-4 no-underline transition-all hover:bg-blue-500/40 dark:bg-blue-100-dark dark:hover:bg-blue-600/40 md:p-5 min-[1920px]:p-6" | |
| > | |
| <FontAwesomeIcon | |
| icon={faChartBar} | |
| className="text-2xl text-blue-700 dark:text-blue-700-dark md:text-3xl min-[1920px]:text-4xl" | |
| /> | |
| <span className="text-lg font-medium md:text-xl min-[1920px]:text-2xl"> | |
| {`${t("miniBench")} ${t("tournaments")}`} |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@front_end/src/app/`(main)/aib/components/page-view-2026-summer.tsx around
lines 144 - 156, Replace the hardcoded CTA labels inside the JSX (the "Summer
2026 Bot Tournament" and "MiniBench Tournaments" text nodes) with localized
strings using useTranslations(); e.g., import and call const t =
useTranslations('<appropriateNamespace>') at the top of the
page-view-2026-summer component and replace the span contents with
t('summer2026BotTournament') and t('miniBenchTournaments'); then add those keys
to the locale dictionaries so translations are available. Ensure you use the
same namespace as other AIB strings to keep consistency.
Source: Learnings
| <Link target="_blank" href={"/terms-of-use/"}> | ||
| {chunks} | ||
| </Link> | ||
| ), | ||
| privacy: (chunks) => ( | ||
| <Link target="_blank" href={"/privacy-policy/"}> | ||
| {chunks} |
There was a problem hiding this comment.
Harden external links opened in a new tab.
Add rel="noopener noreferrer" wherever target="_blank" is used.
Proposed fix
- <Link target="_blank" href={"/terms-of-use/"}>
+ <Link
+ target="_blank"
+ rel="noopener noreferrer"
+ href={"/terms-of-use/"}
+ >
{chunks}
</Link>
...
- <Link target="_blank" href={"/privacy-policy/"}>
+ <Link
+ target="_blank"
+ rel="noopener noreferrer"
+ href={"/privacy-policy/"}
+ >
{chunks}
</Link>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <Link target="_blank" href={"/terms-of-use/"}> | |
| {chunks} | |
| </Link> | |
| ), | |
| privacy: (chunks) => ( | |
| <Link target="_blank" href={"/privacy-policy/"}> | |
| {chunks} | |
| <Link | |
| target="_blank" | |
| rel="noopener noreferrer" | |
| href={"/terms-of-use/"} | |
| > | |
| {chunks} | |
| </Link> | |
| ), | |
| privacy: (chunks) => ( | |
| <Link | |
| target="_blank" | |
| rel="noopener noreferrer" | |
| href={"/privacy-policy/"} | |
| > | |
| {chunks} | |
| </Link> |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@front_end/src/app/`(main)/aib/components/page-view-2026-summer.tsx around
lines 176 - 182, The Link elements in page-view-2026-summer.tsx that use
target="_blank" (the JSX fragments rendering terms and privacy links) are
missing rel attributes; update those Link usages to include rel="noopener
noreferrer" alongside target="_blank" to harden external links opened in a new
tab (locate the Link instances inside the terms and privacy render
functions/components and add the rel attribute).
First pass on # 4857.
/aib/2026/summerpage + dedicated hero/cards/page-view components.NextTournamentCard+ Q1/Q2 2025 next-tournament links to Summer 2026.FABSeasonSummer,FABStartDateMay4i18n strings and updateFABTournamentUnderway+startCompetingDescription.Follow-ups listed in the issue comment: demote Spring 2026 to its own components, run the translation script, confirm image asset + copy/dates/prize, run
bun run lint:types.Generated with Claude Code
Summary by CodeRabbit
New Features
Updates