--- 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