partial (source-backed; frontend/browser runtime evidence remains open)

Spec: F62 — TanStack Router (Type-Safe Routing)

Status: partial (source-backed; frontend/browser runtime evidence remains open)

1. Problem & CEO Job

See clearly (job 1). The application needs one auditable routing source of truth for dynamic paths, redirects, lazy boundaries, and navigation. TanStack Router provides typed route construction and navigation while the compatibility shim allows the existing page call sites to migrate incrementally.

Implementation detail, not user-facing. Zero visible change to the CEO.

2. User Stories & Acceptance Criteria

  • As a developer, I want type-safe route params and search params, so that missing or malformed params produce compile errors instead of runtime crashes.

    • Given a route with /:id, accessing route.id is typed as string, never string | undefined — proved by $param syntax in routeTree.ts and declare module '@tanstack/react-router' Register
    • Given a search parameter ?status=open, the route’s search type is checked at compile time — Zod search schemas on /work/tasks and /crm/deals routes
  • As a developer, I want loader data automatically typed across route boundaries, so that I never cast any from a loader to a component.

    • Given a route loader returning Task[], the component reads data as Task[] — loader on /work/today wired in Phase 2
  • As a developer, I want to be able to co-locate routes with their data dependencies (TanStack Query integration), so that prefetching and loading states are handled declaratively.

    • Given a route, I can call queryClient.ensureQueryData in a loader and the component reads cached data — pending Phase 3 runtime cutover

3. Scope Boundaries

  • In: Installation, route tree definition, typed dynamic paths, legacy redirects, lazy route components, intent/pre-main matched-route preload wiring, and incremental migration of existing page call sites
  • Out: Nested layouts/outlets beyond what we already have, code-splitting per route (not beneficial at current scale), SSR (we’re a SPA)

4. Data Model

n/a — no models touched.

5. API Contract

n/a — routing layer only. No new API endpoints.

6. UI / UX

  • Route tree: frontend/src/routes/routeTree.ts — the canonical flat/nested tree with 107 createRoute(...) declarations, $param paths, auth guards, redirects, and a registered router
  • Router entry: frontend/src/main.tsx renders TanStack RouterProvider with the exported router; frontend/src/lib/router.tsx is a compatibility shim over TanStack primitives for incremental call-site migration
  • Pre-main route warm-up: earlyAuthenticatedRoutePreloadEntry.ts is a separate Vite entry that advances Layout and the matched authenticated P0 leaf only when the existing non-secret session hint is present and logout is not pending; it does not guard routes or start data requests
  • Migration strategy: route definitions and boot are on TanStack Router. Current source inspection found zero react-router-dom imports under frontend/src; 94 page/component files use the local compatibility shim. Query-loader integration remains a separate future boundary.
  • States: 404 catch-all route renders NotFoundPage; loader error shows toast + fallback
  • Components used: Link (typed, to props compile-checked); router.navigate({ to: '...' }) compile-checked
  • i18n: n/a — routing keys stay in sidebar namespace

7. Test Plan

#CaseTypeWhere
1Typed route params compile correctlytypetsc --noEmit
2Invalid route param access is caught at compile timetypetsc --noEmit
3All existing links still navigate correctlye2elumi.spec.ts (existing describe blocks)

Migration is incremental — no single “ship” gate. Existing tests must pass at every step.

8. Current Behavior

The current source has 107 createRoute(...) declarations, a createRouter({ routeTree }) registration, typed dynamic route construction, legacy redirects, lazy intent preloaders, and a separate pre-main matched-route warm-up entry. Focused Vitest and production-build checks execute locally; browser navigation and deployed behavior remain explicit evidence gaps. Query-loader integration remains pending as a separate implementation phase.

9. Error States

  • 404: TanStack Router’s catch-all route renders NotFoundPage (existing)
  • Loader error: TanStack Router’s onError handler shows a toast + fallback content
  • Compile error on migration: tsc --noEmit catches undefined params before deploy

10. Diagrams & Docs Touched

  • frontend/DIAGRAMS.md §1 route tree — update schema to show typed links
  • frontend/AGENTS.md — add routing convention

11. Implementation Audit

#ClaimStatusEvidence
1@tanstack/react-router installed✅ matchfrontend/package.json
2Route tree at frontend/src/routes/routeTree.ts with createRootRoute + typed leaf routes✅ matchrouteTree.ts contains 107 createRoute(...) declarations and $param paths
3Dynamic params use $param syntax (/work/projects/$id/board, /accept-invitation/$token)✅ matchPhase 2; routeTree.ts
4router.navigate({ to: '...' }) compile-time checked✅ source matchtanstack-router.test.ts; no current command-bound tsc result retained
5Route-tree and preload tests registered✅ passFocused router selection 139/139 plus production build on 2026-07-16; browser execution remains open
6frontend/DIAGRAMS.md §1 updated with Phase 2 note✅ matchfrontend/DIAGRAMS.md
7Browser navigation runtime execution⚠️ gapRouter Vitest and production build passed locally; the registered Playwright navigation suite is not retained as commit-bound browser evidence

12. Open Questions / Decisions

  • Whether to use file-based routing (@tanstack/router-plugin) or flat file route tree
  • Decision: Keep the current routeTree.ts source of truth. It is larger than the original estimate, but source inspection and registered tests are aligned to the live tree; splitting it is outside this slice.

13. Bug / Change Log

  • 2026-07-16: Added a build-separated pre-main entry for remembered authenticated P0 routes. It advances only Layout and the matched leaf, skips public/unknown paths and pending logout, and leaves refresh/router authorization unchanged. Build and unit guards enforce entry separation and ordering.
  • 2026-07-16: Contract Graph audit reconciled the stale BrowserRouter/react-router-dom claims with current source. main.tsx uses TanStack RouterProvider; no app-source react-router-dom imports remain. Browser and query-loader runtime evidence remain explicit gaps.
  • 2026-06-15: Frontend route namespaced: /today -> /work/today, /tasks -> /work/tasks, /projects/$id/board -> /work/projects/$id/board, /deals -> /crm/deals (app-wide IA restructure; API paths unchanged).
  • 2026-06-14: SP-C audit. Corrected §6 route tree path (routes.ts → routeTree.ts), updated §2 ACs to reflect Phase 1+2 shipped status, fixed §8 version (v6 → v7). Phase 3 (runtime cutover, 77 react-router-dom imports) is the remaining gap.