Files
Lucas Berger 982438dc10 style(13-03): apply Prettier formatting across repo
Mechanical reformat — no logic changes. 398 files changed, 19125
insertions(+), 16457 deletions(-). Prettier 3.8.4 with .prettierrc
(singleQuote:true, semi:true, tabWidth:2, trailingComma:all,
printWidth:100). Isolated per D-13-08 for reviewability.
2026-06-11 20:35:18 -04:00

9.2 KiB

phase, plan, subsystem, tags, dependency_graph, tech_stack, key_files, decisions, metrics
phase plan subsystem tags dependency_graph tech_stack key_files decisions metrics
04-shared-lists-live-sync 03 api-routes, pwa-components
lists-crud
scoped-access
D-01
D-04
D-06
tdd
optimistic-ui
list-01
requires provides affects
04-01 (lists/list_shares schema, test harness, BrowserRouter shell)
04-02 (listAccess.ts, listEmitter.ts primitives)
POST/GET/PATCH/DELETE /api/lists with scoped access (D-04) and auto-share (D-01/D-02)
listsRouter mounted at /api/lists in index.ts
ListsIndex wired to real data (useQuery + useMutation)
ListCard with name/count badge/Shared pill + hover-reveal delete
CreateListSheet (Shared default D-01, optimistic useMutation)
ListDeleteDialog (mirrors Phase 3 pattern, props-driven, D-06)
ListsEmptyState (standalone component)
listsClient
fetchLists/createList/patchList/deleteList + List/ListItem types
apps/api/src/routes/lists.ts (new)
apps/api/src/index.ts (listsRouter mount added)
apps/api/tests/routes/lists.test.ts (it.todo stubs replaced with real assertions)
apps/pwa/src/api/listsClient.ts (expanded with create/patch/delete)
apps/pwa/src/routes/ListsIndex.tsx (rewritten with real data)
added patterns
resolveUserId helper copied verbatim from events.ts (per-router duplication convention)
getAccessibleListIds via two-select+Set for D-04 scoped GET
Auto-share on create
INSERT list_shares for all users WHERE id != creator (OQ-3/D-01/D-02)
Plan 06 SSE seam comments at every mutation handler (publishListEvent)
useMutation with optimistic update + onError rollback + onSettled invalidate
Props-driven ListDeleteDialog (not Zustand-coupled) to avoid modifying stable calendarStore dialog
created modified
apps/api/src/routes/lists.ts
apps/pwa/src/components/ListCard.tsx
apps/pwa/src/components/CreateListSheet.tsx
apps/pwa/src/components/ListDeleteDialog.tsx
apps/pwa/src/components/ListsEmptyState.tsx
apps/api/src/index.ts (listsRouter import + app.route mount)
apps/api/tests/routes/lists.test.ts (it.todo stubs replaced with 23 real integration tests)
apps/pwa/src/api/listsClient.ts (createList/patchList/deleteList + List type expanded)
apps/pwa/src/routes/ListsIndex.tsx (rewritten — real data, ListCard, CreateListSheet, ListDeleteDialog)
D-04 GET scoped: two-select + Set union (owner + list_shares) matches listAccess.ts pattern
DELETE owner-only: safe default per plan spec; sharees can edit but not delete in LIST-01
ListDeleteDialog is props-driven (not Zustand) to keep calendarStore dialog untouched (stable)
Plan 06 SSE seam comments left at every mutation handler (publishListEvent not yet wired)
dev-user (id=1) must exist in users table for dev bypass to work with write endpoints (pre-existing env constraint)
[Rule 1] @hono/zod-validator returns 400 (not 422); tests corrected to match events.ts convention
duration completed task_count file_count
~12 minutes 2026-06-09 2 9

Phase 4 Plan 3: List CRUD Vertical Slice Summary

One-liner: Full lists CRUD vertical slice (LIST-01) — POST/GET/PATCH/DELETE /api/lists with D-04 scoped access + auto-share-on-create, wired to ListsIndex/ListCard/CreateListSheet/ListDeleteDialog UI with optimistic mutations.

TDD Gate Compliance

Gate Commit Status
RED — 23 failing integration tests 2b3d789 PASS — all 23 failed (404, router not mounted)
GREEN — listsRouter + index mount 9546b74 PASS — all 23 tests pass
REFACTOR (skipped) Implementation was clean on first pass

Tasks Completed

Task Name Commit Files
RED Failing lists route integration tests 2b3d789 tests/routes/lists.test.ts
GREEN listsRouter implementation + index mount + test corrections 9546b74 lists.ts, index.ts, lists.test.ts
2 UI: listsClient + ListsIndex + ListCard + CreateListSheet + ListDeleteDialog + ListsEmptyState 95dbc66 6 files (4 new, 2 modified)

Deviations from Plan

Auto-fixed Issues

1. [Rule 1 - Bug] @hono/zod-validator returns HTTP 400, not 422

  • Found during: GREEN phase — 4 zod validation tests failed with expected 422 to be 400
  • Issue: The plan specified 422 for zod validation failures, but @hono/zod-validator returns 400 (matching the existing events.ts convention in the codebase).
  • Fix: Updated test assertions to expect 400, with an inline comment explaining the choice is consistent with events.ts convention.
  • Files modified: apps/api/tests/routes/lists.test.ts
  • Commit: 9546b74

Playwright Browser Check

Ran against http://localhost:5173/lists with API on http://localhost:3000 (DEV_AUTH_BYPASS=true):

  1. /lists renders empty state: "No lists yet" + "Tap + to create your first shared list…" — PASS
  2. Click "+ New list" FAB → CreateListSheet opens with name input auto-focused, Shared/Private toggle defaulting to Shared, Create button disabled (empty name) — PASS
  3. Type "Groceries" → Create → sheet closes, card appears with "Shared" pill and "0 items" — PASS
  4. Create "Gift Ideas" → second card appears — PASS
  5. Hover "Gift Ideas" card → delete (X) icon appears → click → ListDeleteDialog opens with correct heading + body text — PASS
  6. Click "Delete" → dialog closes, "Gift Ideas" card disappears, only "Groceries" remains — PASS

Verification Results

API Tests

  • tests/routes/lists.test.ts: 23 passed (0 failed)
  • D-04 negative test ("does NOT return private list of another user") — GREEN
  • All create/get/delete/patch/scope assertions green

Full API Suite

  • 16 passed | 2 skipped (Wave-0 stubs) | 140 passed | 22 todo — no regressions

TypeScript

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

Existing Tests

  • apps/pwa/src/components/DeleteConfirmationDialog.test.tsx — 10 passed (regression guard green)
  • DeleteConfirmationDialog.tsx NOT modified

Known Stubs

File Stub Reason
apps/pwa/src/routes/ListsIndex.tsx:66 // TODO: surface "Couldn't delete. Try again." toast Plan 06 adds the notification layer once SSE and toast pattern are established
apps/api/src/routes/lists.ts Plan 06 SSE seam comments (publishListEvent calls commented out) Plan 06 adds fan-out once the SSE /api/sse/lists endpoint exists

Neither stub prevents the plan's goal (create + delete named lists). Both are forward-seam comments, not data gaps.

Threat Surface Scan

All threats from the plan's threat model are mitigated:

Threat ID Status Notes
T-04-05 (EoP — private list via direct id) Mitigated checkListAccess() on every mutation; 403 tested
T-04-02 (Info Disclosure — GET leaking non-shared lists) Mitigated Two-select + Set scope; negative test asserts absence
T-04-06 (Tampering — XSS via list name) Mitigated All list names plain-text JSX children; no dangerouslySetInnerHTML
T-04-07 (Tampering — overposting on PATCH) Mitigated patchListSchema whitelists name/isShared only; 400 tested
T-04-08 (EoP — self-adding to list_shares) Mitigated Shares server-managed only; no client-writable shares endpoint

No new threat surface beyond the plan's trust boundaries.

Self-Check: PASSED

  • apps/api/src/routes/lists.ts — FOUND
  • apps/api/src/index.ts (listsRouter mounted) — FOUND (grep: "app.route('/api/lists'")
  • apps/pwa/src/api/listsClient.ts (createList exported) — FOUND
  • apps/pwa/src/components/ListCard.tsx — FOUND
  • apps/pwa/src/components/CreateListSheet.tsx — FOUND
  • apps/pwa/src/components/ListDeleteDialog.tsx — FOUND
  • apps/pwa/src/components/ListsEmptyState.tsx — FOUND
  • apps/pwa/src/routes/ListsIndex.tsx — FOUND (rewritten)
  • Commit 2b3d789 (RED) — FOUND
  • Commit 9546b74 (GREEN) — FOUND
  • Commit 95dbc66 (Task 2 UI) — FOUND