Skip to content

feat(games): launch games, indexable with IGDB credit and no flag - #61

Merged
kris1027 merged 18 commits into
mainfrom
feat/games-launch
Oct 27, 2026
Merged

kris1027 merged 18 commits into
mainfrom
feat/games-launch

Conversation

@kris1027

@kris1027 kris1027 commented Oct 26, 2026 •

Copy link
Copy Markdown
Owner

What

Games are now open to everyone. Game pages, the /games landing and its five list pages can be indexed, carry canonicals and share cards, and are listed in the sitemap. Game pages also get VideoGame JSON-LD. The footer and /terms credit IGDB next to TMDB. The NEXT_PUBLIC_GAMES_LIBRARY flag is deleted, so the GAMES tab and ?type=game always work.

Why

This builds scope feature G10 (GA tier) and follows spec 0032. Games should reach search and link previews at the same quality bar as movies and shows, through the same SEO and legal modules. IGDB's free, non commercial terms ask for a visible credit with a clear path back to IGDB. Deleting the flag, rather than flipping it, leaves no dead branch to keep tested and makes Preview match Production.

Changes

SEO

  • A found game page (/games/{id}) is indexable through catalogMetadata(). It gets its canonical, a share image (art, then the first screenshot, then the cover, then the site card) and gameJsonLd(), a schema.org VideoGame with developers as author and publishers as publisher. It never carries a rating of any kind (AC-1, AC-3).
  • A malformed, unknown, hidden or failed game page stays noindex, with no canonical, no og:url, no JSON-LD and nothing game specific. AC-2 was amended in this PR so these pages may keep the root layout's default site card, the same as /movies/{id} (AC-2).
  • /games gets its canonical. The five games list pages swap static metadata for generateMetadata through catalogListMetadata, which reuses the page's own list read (one shared cache entry, no extra IGDB request). The discover callback now returns { hasResults, lastPage }, so a games list with no IGDB count is indexed on any page that shows games. TMDB lists behave exactly as before (AC-4, AC-5).
  • sitemap.xml adds /games, the five games list pages and the first 200 Popular games (GAME_SITEMAP_LIMIT). If IGDB fails, only the game ids drop and the sitemap caches for minutes instead of days. An IgdbValidationError still fails the build on purpose (AC-6, AC-7).
  • No SEO surface reads cookies, headers, the session or the visitor's region. request-scope.test.ts now also keeps the visitor region out of the games pages (AC-14).

Legal and IGDB terms

  • IGDB_ATTRIBUTION ("Game data and images from IGDB.com") and IGDB_HOME_URL live only in lib/legal/operator.ts, and lib/legal/boundary.test.ts guards them. The footer shows IGDB's official press kit logo (public/igdb-logo.svg, hash checked) with the credit as one link. /terms becomes "Catalog data, TMDB and IGDB" and gains three IGDB paragraphs. LEGAL_LAST_UPDATED is bumped (AC-8 to AC-10).
  • The IGDB terms check (API docs, FAQ, Twitch Developer Services Agreement, press kit) is recorded with quotes and dates in rationale.md, IGDB terms check. The verdict is that BeStats complies. To stay inside Twitch's 24 hour caching clause, getGameGenres and getGamePlatforms now cache for hours instead of max (AC-11).

Flag removal

  • Removed: getGamesLibrary and gamesLibraryEnabled from lib/env.ts, lib/search/games-flag.ts, and every gamesEnabled argument and option across lib/catalog/media-type.ts, lib/search/params.ts, lib/search/quick.ts and their callers. The variable is also gone from .env.example. security-boundary.test.ts fails if any tracked file outside docs/ names it again (AC-12).

Deploy

  • docs/deploy.md section 4 lists IGDB_CLIENT_ID and IGDB_CLIENT_SECRET for Production and Preview and drops the flag row. Both credentials are set in Vercel Preview as sensitive, and the flag is removed from Preview (AC-13).

How to test / verify

Checks already run (see verify.md): pnpm test green, pnpm build shows /games, /games/[id] and the five list pages as partially prerendered with /sitemap.xml, /robots.txt and /opengraph-image static, vercel env ls shows the IGDB credentials in Production and Preview with no flag, and a Preview vercel curl /games/1942 renders the game.

If you'd like to check it yourself, these steps cover it:

  1. Build locally as indexable: VERCEL_ENV=production NEXT_PUBLIC_SITE_URL=https://bestats.example pnpm build, then PORT=3123 pnpm start with the same two variables.
  2. Open /games/1942. You should see a canonical, no robots meta, an og:image set to the game's art and one VideoGame script with no rating.
  3. Open /games/999999999. It should be noindex, with no canonical, no og:url and no JSON-LD.
  4. Open /games/popular and /games/recently-released?page=3. Each should have its own canonical, while /games/top-rated?page=51 and ?page=abc should be noindex.
  5. Open /sitemap.xml. It should list /games, the five games list pages after the movie and show ones, and up to 200 /games/{id} at the end, all absolute.
  6. Check the footer at 1440px and 390px for the IGDB logo and credit linking to igdb.com in a new tab, and check /terms for the three IGDB paragraphs.
  7. With no flag set, check that the GAMES tab shows on /shows and that /search?type=game&q=zelda and GET /api/search?type=game&q=zelda return games. (This step is still open in verify.md.)
  8. After merge, run the AC-15 production checks: the sitemap lists games, /games/1942 has a canonical, og:image and JSON-LD, and validator.schema.org finds a VideoGame with zero errors.

Risk & rollout

No migrations and no schema change. Nothing from IGDB is stored. The flag is gone, so there is no kill switch: if the launch goes wrong, you'd undo it with Vercel Instant Rollback (docs/deploy.md, section 8) and then fix forward. After a rollback, game URLs that search engines already hold keep resolving, because the pages exist without the launch.

Accepted risk (spec 0032, migration plan): crawlers hitting 200 game pages meet IGDB's 4 requests a second limit. getGame is cached for hours and a 429 shows as the failed branch, which is noindex, so a retry panel is never indexed. A crawler that lands there may drop that game until its next crawl.

List page metadata now waits on the page's IGDB read, so a slow IGDB delays the head. It shares the page's cache entry, so it adds no extra request.

Notes for reviewers

  • The IGDB terms were read by the agent in a real browser, not by a person. rationale.md suggests a quick human pass over the quotes before the production deploy.
  • AC-2 was amended mid PR (commit 9954412) after /check verify found that not found pages keep the root layout's default site card. The rule that holds is that nothing game specific appears.
  • The flag removal touches many files and tests at once. Most of it is deleted branches. The parts worth extra eyes are lib/catalog/media-type.ts and lib/search/params.ts, where game is now always accepted.
  • After merge, /sync should update the root AGENTS.md games and flag lines and lib/seo/AGENTS.md (spec 0032, Follow up).

🤖 Generated with Claude Code

kris1027 and others added 11 commits October 26, 2026 20:59
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…h's 24 hour clause

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… VideoGame JSON-LD

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…es in the sitemap

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hare cards

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rop the games flag

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ot found pages

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kris1027 kris1027 added docs Documentation additions and updates feature New user-facing functionality test Automated tests and test infrastructure ui Layouts, styling, and shared interface components labels Oct 26, 2026
@kris1027 kris1027 self-assigned this Oct 26, 2026
@github-project-automation github-project-automation Bot moved this to Backlog in BeStats Oct 26, 2026
@vercel

vercel Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
bestats Ready Ready Preview Oct 27, 2026 8:27am UTC

kris1027 and others added 4 commits October 27, 2026 09:19
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kris1027 and others added 3 commits October 27, 2026 09:25
Spec 0032, AC-9 sets LEGAL_LAST_UPDATED to the date of the PR's final
commit, which is now 2026-10-27.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ments

Callers flatten their pages, so the games list no longer wraps itself in
a page just to reuse the helper. The JSDoc lines left long by the games
change are rewrapped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
lib/seo/AGENTS.md now names gameJsonLd, GAME_SITEMAP_LIMIT, the unpaged
/games landing and the IGDB sitemap read. The root legal facts bullet
records IGDB_ATTRIBUTION and the hash checked IGDB logo.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kris1027
kris1027 merged commit f2827c0 into main Oct 27, 2026
4 checks passed
@github-project-automation github-project-automation Bot moved this from In review to Done in BeStats Oct 27, 2026
@kris1027
kris1027 deleted the feat/games-launch branch October 27, 2026 09:18

This branch was successfully deployed

1 active deployment
Preview — 93c2aec3 Deployed Oct 27, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Documentation additions and updates feature New user-facing functionality test Automated tests and test infrastructure ui Layouts, styling, and shared interface components

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant