---
phase: 04-shared-lists-live-sync
plan: '01'
subsystem: pwa-routing, db-schema, test-harness
tags: [react-router, bottom-tab-bar, lists-surface, drizzle-migration, wave-0-red-stubs]
dependency_graph:
  requires: []
  provides:
    - BrowserRouter shell with /calendar, /lists, /lists/:listId routes
    - BottomTabBar + AppNav desktop Lists link
    - ListsIndex empty surface
    - lists/list_items/list_shares Drizzle tables (migrated)
    - API Vitest test harness (setup.ts + vitest.config setupFiles)
    - Wave-0 RED stub test files (4 files, 12+44 todo items)
  affects:
    - apps/pwa/src/App.tsx (router wrapping)
    - apps/pwa/src/components/AppNav.tsx (desktop nav links)
    - apps/pwa/src/components/CalendarShell.test.tsx (MemoryRouter fix)
    - apps/api/src/db/schema.ts (new tables)
tech_stack:
  added:
    - react-router@7.17.0 (declarative BrowserRouter mode)
    - '@dnd-kit/core (installed, used in later plans)'
    - '@dnd-kit/sortable (installed, used in later plans)'
    - fractional-indexing (PWA + API)
  patterns:
    - NavLink with isActive style callback (BottomTabBar + AppNav desktop)
    - TanStack Query for list data fetching (ListsIndex)
    - Zustand UI-only store (listsStore: no server data)
    - drizzle-kit generate+migrate (NOT push) for DDL
    - it.todo() Wave-0 stub pattern (RED stubs safe to import)
key_files:
  created:
    - apps/pwa/src/App.tsx (rewritten — BrowserRouter shell)
    - apps/pwa/src/components/BottomTabBar.tsx
    - apps/pwa/src/routes/ListsIndex.tsx
    - apps/pwa/src/routes/ListDetail.tsx (placeholder stub)
    - apps/pwa/src/store/listsStore.ts
    - apps/pwa/src/api/listsClient.ts (fetchLists + List/ListItem types)
    - apps/api/test/setup.ts
    - apps/api/tests/routes/lists.test.ts
    - apps/api/tests/lib/listEmitter.test.ts
    - apps/pwa/src/hooks/useListSSE.test.ts
    - apps/pwa/src/routes/ListDetail.test.tsx
    - apps/api/src/db/migrations/0002_lists_schema.sql
  modified:
    - apps/pwa/src/components/AppNav.tsx (added NavLink imports + desktop Lists nav link)
    - apps/pwa/src/components/CalendarShell.test.tsx (MemoryRouter wrapper)
    - apps/api/src/db/schema.ts (lists, listShares, listItems tables appended)
    - apps/api/vitest.config.ts (setupFiles → apps/api/test/setup.ts)
    - apps/pwa/package.json (react-router, @dnd-kit/core, @dnd-kit/sortable, fractional-indexing)
    - apps/api/package.json (fractional-indexing)
decisions:
  - 'D-17 satisfied: react-router@7 declarative BrowserRouter (no data router/loaders)'
  - 'D-16 satisfied: BottomTabBar with Calendar + Lists NavLinks at /calendar and /lists'
  - 'generate+migrate enforced: 0002_lists_schema.sql is additive (CREATE TABLE only, no DROP)'
  - 'ioredis NOT added (fan-out is in-memory EventEmitter per D-04, Plan 02)'
  - 'Wave-0 RED stubs use it.todo() to be safe-to-import without implementations'
  - 'CalendarShell.test.tsx wrapped in MemoryRouter after AppNav gained NavLink (Rule 1 fix)'
metrics:
  duration: '~65 minutes (continuation agent, prior executor completed Tasks 1-2)'
  completed: '2026-06-09'
  task_count: 4
  file_count: 17
---

# Phase 4 Plan 1: Foundation Shell Summary

**One-liner:** React-router BrowserRouter shell + BottomTabBar + empty Lists surface; three list tables migrated to MariaDB; Wave-0 RED test stubs in place.

## Tasks Completed

| Task | Name                                                               | Commit                    | Files                                                                                                                        |
| ---- | ------------------------------------------------------------------ | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| 1    | Package legitimacy gate (human-verify)                             | — (checkpoint, prior run) | —                                                                                                                            |
| 2    | Install new deps + scaffold API test harness with Wave-0 RED stubs | 39d4ec8                   | package.json ×2, setup.ts, vitest.config.ts, 4 test files                                                                    |
| 3    | Add list tables to schema + generate+migrate [BLOCKING]            | 2f25b15                   | schema.ts, 0002_lists_schema.sql, drizzle journal                                                                            |
| 4    | Add react-router + BottomTabBar + empty ListsIndex shell           | c0088ed                   | App.tsx, BottomTabBar.tsx, ListsIndex.tsx, ListDetail.tsx, listsStore.ts, listsClient.ts, AppNav.tsx, CalendarShell.test.tsx |

## Deviations from Plan

### Auto-fixed Issues

**1. [Rule 1 - Bug] Wave-0 RED test stubs missing vitest imports**

- **Found during:** Task 4 verification
- **Issue:** `apps/pwa/src/hooks/useListSSE.test.ts` and `apps/pwa/src/routes/ListDetail.test.tsx` used bare `describe`/`it` without importing from `vitest`. TypeScript raised TS2582 errors; the files would not run in the test harness.
- **Fix:** Added `import { describe, it } from 'vitest'` to both files following the same pattern as `apps/pwa/src/api/client.test.ts`.
- **Files modified:** `apps/pwa/src/hooks/useListSSE.test.ts`, `apps/pwa/src/routes/ListDetail.test.tsx`
- **Commit:** c0088ed

**2. [Rule 1 - Bug] CalendarShell.test.tsx broke after AppNav gained NavLink**

- **Found during:** Task 4 verification (CalendarShell test run)
- **Issue:** Adding NavLink to AppNav's DesktopNav required a Router context. The existing `CalendarShell.test.tsx` rendered `<CalendarShell />` directly without any Router wrapper, causing all 6 tests to fail with `useLocation() may be used only in the context of a <Router> component`.
- **Fix:** Added `import { MemoryRouter } from 'react-router'` and wrapped `renderWithClient`'s render call in `<MemoryRouter initialEntries={['/calendar']}>`. All 6 tests pass again.
- **Files modified:** `apps/pwa/src/components/CalendarShell.test.tsx`
- **Commit:** c0088ed

## Verification Results

### TypeScript

- `pnpm --filter @familysync/api typecheck` — PASS
- `pnpm --filter @familysync/pwa exec tsc --noEmit` — PASS

### Tests

- API Wave-0 stubs: 108 passed, 44 todo (RED stubs, as expected)
- PWA Wave-0 stubs (`useListSSE.test.ts`, `ListDetail.test.tsx`): 12 todo (as expected)
- `CalendarShell.test.tsx`: 6 passed (regression guard green)

### Migration

- `0002_lists_schema.sql` is additive: CREATE TABLE only for `lists`, `list_shares`, `list_items`
- No DROP/TRUNCATE of pre-existing tables
- `db:migrate` applied via `pnpm --filter @familysync/api db:migrate`

### Playwright Browser Check (per CLAUDE.md convention)

Navigated to `http://localhost:5173/lists` (DEV_AUTH_BYPASS active, DB not running locally):

- "Lists" heading rendered (`<h1>Lists</h1>`)
- "New list" button present (placeholder FAB)
- BottomTabBar visible with Calendar (`/calendar`) and Lists (`/lists`) NavLinks
- Loading state shown ("Loading lists…") — expected; `/api/lists` returns 404 until Plan 04-02 mounts the route
- No unexpected errors (favicon.ico 404 and `/api/lists` 404 are both expected at this stage)

### vite.config.ts navigateFallback

Verified: `navigateFallbackDenylist` only excludes `/^\/callback/`, `/^\/api\//`, `/^\/health/`. The `/lists/*` paths are NOT in the denylist — SPA deep-links to `/lists/:listId` will be served by the SW correctly.

## Known Stubs

| File                                     | Stub                                                              | Reason                                            |
| ---------------------------------------- | ----------------------------------------------------------------- | ------------------------------------------------- |
| `apps/pwa/src/routes/ListDetail.tsx`     | Full list detail UI (placeholder renders "List view coming soon") | Plan 04-04 implements items, SSE, drag-to-reorder |
| `apps/pwa/src/routes/ListsIndex.tsx` FAB | `onClick` is a no-op                                              | Plan 04-03 wires `CreateListSheet`                |
| `apps/pwa/src/api/listsClient.ts`        | Only `fetchLists` exists; no create/delete/item CRUD              | Plans 04-02/04-03 expand                          |

These stubs intentionally leave the surface navigable but empty — subsequent plans fill in the data and interaction layer.

## Threat Surface Scan

No new trust boundaries introduced. `listsClient.ts` makes `GET /api/lists` calls (no credentials beyond what existing `client.ts` establishes — same `credentials: 'include'` pattern). T-04-SC (react-router legitimacy) was satisfied by Task 1 human gate.

## Self-Check: PASSED

- `apps/pwa/src/App.tsx` — FOUND
- `apps/pwa/src/components/BottomTabBar.tsx` — FOUND
- `apps/pwa/src/routes/ListsIndex.tsx` — FOUND
- `apps/pwa/src/routes/ListDetail.tsx` — FOUND
- `apps/pwa/src/store/listsStore.ts` — FOUND
- `apps/pwa/src/api/listsClient.ts` — FOUND
- `apps/api/src/db/migrations/0002_lists_schema.sql` — FOUND (committed in 2f25b15)
- Commit 39d4ec8 — FOUND
- Commit 2f25b15 — FOUND
- Commit c0088ed — FOUND
