SEO Indexing — Web Implementation Plan
This directory contains the full, step-by-step implementation plan for SEO Indexing
on the RecordPlus Web product (record-web). It maps directly to the
SEO Indexing PRD.
These documents are written to be executed by an engineer or an AI coding agent with no prior context on this feature. Every task references real files, real patterns already in the codebase, and exact code to add. Follow them in order.
Phase index
| Phase | File | Goal | Blocking external deps |
|---|---|---|---|
| Phase 1 | PHASE-1-CRAWLABILITY.md | Public detail HTML crawlable, protected and technical HTML noindex, valid concrete detail sitemap URLs, metadataBase, pt-BR, and verification support. | Concrete catalogue enumeration is required only for a future catalogue-complete sitemap. |
| Phase 2 | PHASE-2-METADATA-CANONICALS.md | Canonical URLs on every indexable page, complete OG + Twitter tags, per-asset title/description/image on detail pages. | Phase 1 merged. |
| Phase 3 | PHASE-3-STRUCTURED-DATA.md | Safe JSON-LD only on pages whose rendered playback experience and JWX data satisfy the applicable eligibility rules. | OQ-2 JWX fields, OQ-4 Live policy, and logged-out watch-page decision. |
| Phase 4 | PHASE-4-CORE-WEB-VITALS.md | Baseline CWV measurement, hero image / CLS / font / bundle fixes to reach "mostly green" on all indexable page types. | Phase 1 live in production (≥ 28 days CrUX data). |
| Middleware | PHASE-5-MIDDLEWARE-SEO-ENABLEMENT.md | JWX catalogue feed, real modification data, availability, scale, and optional JWX-driven freshness. | Feed is a launch dependency when catalogue-complete discovery is required; other enhancements are conditional. |
Provider note: Record uses JWX. Do not extend the legacy Brightcove webhook path for new SEO work. A middleware companion document does not exist yet; create it with the JWX implementation ticket rather than linking to a nonexistent file.
Architecture cheat-sheet (read once before starting)
record-web is a Next.js 16.2 App Router application (src/app/). The conventions
you must follow:
- Primary dynamic route:
src/app/[[...routeSegments]]/page.tsx— catch-all that resolves the CMS page viagetDynamicRoutes()+getViewFromPage()fromrouteToViewMapper.ts. - Page-type → component mapping:
src/views/routeToViewMapper.tsmaps CMSpageTypestrings to React view components and theirgenerateMetadatafunctions. Detail pages (MovieDetailsPage,ShowDetailsPage) live insrc/views/*/. - Metadata utility:
src/utils/metadata.ts—baseMetadataobject andgetMetadata()helper. All pages should build ongetMetadata(), not construct rawMetadataobjects. - Sitemap:
src/app/sitemap.ts— Next.jsMetadataRoute.Sitemapexport, callsgetDynamicRoutes()for CMS-managed routes. - Robots:
src/app/robots.ts— Next.jsMetadataRoute.Robotsexport. - Root layout:
src/app/layout.tsx— exports themetadataconstant (root defaults) and therevalidateconstant (ISR cache window, from envDEFAULT_CACHE_REVALIDATION). - ISR/SSR: Next.js App Router serves SSR by default. Server Components fetch data
at request time (or from the Next.js cache).
revalidateat segment level controls ISR. - CMS SEO fields: pages provide
page.seo?.metaTitle,page.seo?.metaDescription,page.seo?.metaKeywords. Asset metadata comes from the OVP provider viaServiceManager.getServiceProvider('ovp').
Indexable pages (confirmed Web scope)
| Page type | CMS template / route | Indexable |
|---|---|---|
| Movie Detail | MovieDetailsPage | ✅ |
| Show/Series Detail | ShowDetailsPage | ✅ |
| Podcast Detail | PodcastDetailsPage | ✅ |
| Radio Detail | configured detail route | ✅ |
| Channel Detail | configured detail route | ✅ |
| Other supported details | configured detail route | ✅ |
| Home | Modular | ❌ |
| Category and Listing | Modular | ❌ |
| Search results | /search | ❌ |
| My List | /my-list | ❌ |
| Your Region | /your-region | ❌ |
| Billing and payment success | /billing/*, /payment/*/success | ❌ |
| Technical CMS page | /page/* | ❌ |
| View All query surface | /view-all | ❌ |
| Profile | /profile | ❌ |
| Player | /player | ❌ |
| Payment | /payment | ❌ |
| Subscription | /subscription | ❌ |
| Signup | /signup | ❌ |
| Account | /account | ❌ |
| Login | /login | ❌ |
| EPG | /epg | ❌ |
Signed-out users and crawlers receive the same access result. Detail pages are public; non-detail application entry points redirect to Login.
Open questions that gate work (resolve before the relevant phase)
| ID | Question | Blocks |
|---|---|---|
| OQ-2 | JWX exposes uploadDate, duration, thumbnailUrl, description for VideoObject / TVSeries? | Phase 3 |
| OQ-4 | Live event pages indexed during live window, post-VOD, or both? | Phase 3 (Live JSON-LD) |
| Brand name | Exact product brand string to replace "Assemble Web" defaults | Phase 1 Task 7 |
Definition of done (per phase)
A phase is "done" when:
pnpm buildsucceeds with no TypeScript errors.- Unit tests for new utilities pass (
pnpm test). - The manual QA checklist at the end of that phase passes.
/code-reviewskill reports no BLOCKERs.