---
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 `` directly without any Router wrapper, causing all 6 tests to fail with `useLocation() may be used only in the context of a component`.
- **Fix:** Added `import { MemoryRouter } from 'react-router'` and wrapped `renderWithClient`'s render call in ``. 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 (`Lists
`)
- "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