# Programmatic navigation resolves to pages

- **Pattern:** `ab-000030` (`ai-slop-hallucinations.route-references.programmatic-navigation-resolves`)
- **Severity:** high
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-000030
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-000030)

## Why it matters

Programmatic navigation to a non-existent route is worse UX than a static link to a non-existent route: the user clicks a button, takes a deliberate action, and ends up on a 404 with no breadcrumb back to where they were. `router.push('/admin/users')` when no `app/admin/users/page.tsx` exists is the exact shape of this failure. It breaks post-signup flows, post-payment redirects, and admin dashboards — all critical journeys the audit fixtures test.

## Severity rationale

High because a bad router.push during a critical flow strands the user on a 404 mid-journey after they took an action.

## Remediation

For each unresolved navigation call, either fix the specifier to point at a real route or create the destination file. In Next.js App Router using `useRouter`:

```ts
import { useRouter } from 'next/navigation'
const router = useRouter()
router.push('/admin/team')  // matches app/admin/team/page.tsx
```

If the destination is genuinely missing, scaffold `app/admin/users/page.tsx` so the navigation has a target.

## Detection

- **ID:** `programmatic-navigation-resolves`
- **Severity:** `high`
- **What to look for:** Walk source files for calls to `router.push("...")`, `router.replace("...")`, `router.prefetch("...")`, `redirect("...")`, `permanentRedirect("...")`, `useNavigate()(...)`, `navigate("...")`, `window.location.href = "..."`, `window.location.assign("...")`, `window.location.replace("...")`. Extract the first argument string literal. Apply the same normalization and resolution logic as the previous check. Count all programmatic navigation calls inspected, total resolved, total unresolved.
- **Pass criteria:** 100% of programmatic navigation literal arguments resolve to an existing page route. Report: "X programmatic navigation calls inspected, Y resolved, 0 unresolved."
- **Fail criteria:** At least 1 programmatic navigation literal does not resolve.
- **Do NOT pass when:** A `router.push()` argument is constructed at runtime from a variable AND the base path of the constructed URL does not resolve — the same rule as the fetch-paths check.
- **Skip (N/A) when:** Project has 0 page route files.
- **Detail on fail:** `"2 unresolved navigation calls: router.push('/admin/users') in src/components/AdminMenu.tsx (no app/admin/users/page.tsx exists), redirect('/login') in src/app/dashboard/page.tsx (no app/login/page.tsx)."`
- **Remediation:** Programmatic navigation to non-existent routes results in a runtime 404 after a user action — usually worse UX than a static 404 because it surprises the user. Fix each one to point at an existing route, or create the missing route file:

  ```ts
  // src/components/AdminMenu.tsx
  import { useRouter } from 'next/navigation'

  function AdminMenu() {
    const router = useRouter()
    // Bad: no app/admin/users/page.tsx exists
    // router.push('/admin/users')

    // Good: matches an existing route file
    router.push('/admin/team')
  }
  ```

  Alternatively, create the missing page file at `app/admin/users/page.tsx` so the navigation has a target.

Taxons: reference-integrity

HTML version: https://auditbuffet.com/patterns/ab-000030
