Next.js routing resolution between app/ and pages/ follows non-obvious precedence rules: App Router wins for matching segments, but Pages Router catches everything App Router does not claim. Deploying a half-migrated app can change which route renders simply because someone added or removed a file, with no local indication. SEO metadata, layouts, and data-fetching conventions differ completely between the two routers, producing broken canonical URLs and duplicated or missing OG tags.
Medium because resolution ambiguity can swap which page renders at a given URL without any code change to that page.
Finish the migration to App Router. Enumerate what remains:
find pages -type f -name "*.tsx" | grep -vE '_app|_document|_error|api/'
Move each remaining page into app/ (e.g., pages/about.tsx becomes app/about/page.tsx), port getServerSideProps to server components or route handlers, then delete the old file. Keep pages/api/ until those endpoints are migrated to app/api/ route handlers. Redeploy and verify every URL resolves through the expected router.
ID: ai-slop-code-drift.convention-drift.routing-pattern-consistency
Severity: medium
What to look for: When the framework is Next.js, check whether BOTH app/ AND pages/ directories exist at the project root or under src/. Enumerate every file under each. Pages Router files (pages/index.tsx, pages/about.tsx, etc.) and App Router files (app/page.tsx, app/about/page.tsx, etc.) are mutually exclusive routing systems. EXCEPT: a pages/ directory containing ONLY _app.{tsx,jsx,ts,js}, _document.{tsx,jsx,ts,js}, _error.{tsx,jsx,ts,js}, OR pages/api/** files (legacy API routes are allowed alongside App Router) is acceptable. Count all non-allowed files in pages/ when app/ also exists. The threshold for failure is at least 1 non-allowlisted file in pages/ when app/ also has page files.
Pass criteria: When framework is Next.js: only one routing system in active use, OR app/ is the active router AND pages/ only contains the allowlisted exception files. Report even on pass: "Routing pattern: App Router (or Pages Router). [N] route files."
Fail criteria: Both app/ and pages/ contain non-allowlisted route files — mid-migration state with unclear ownership.
Skip (N/A) when: Framework is not Next.js OR only one of app//pages/ exists.
Detail on fail: "Both routing patterns active: app/ has 12 page files, pages/ has 5 page files (excluding _app, _document, api/). The two routers conflict — pick one and finish the migration."
Remediation: Mixing App Router and Pages Router pages means each route's behavior depends on which router resolves it first — and Next.js's resolution rules are not obvious. Pick the destination and finish the migration:
# Inventory non-allowlisted pages/ files
find pages -type f -name "*.tsx" | grep -vE '_app|_document|_error|api/'
# Move each to app/ following the new conventions
# pages/about.tsx → app/about/page.tsx
# Then delete the pages/ file