| 04-shared-lists-live-sync |
04 |
api-routes, pwa-components |
| item-crud |
| fractional-rank |
| optimistic-ui |
| D-05 |
| D-06 |
| D-07 |
| D-08 |
| D-09 |
| D-13 |
| tdd |
| list-02 |
|
| requires |
provides |
affects |
| 04-01 (list_items schema, BrowserRouter, react-router) |
| 04-02 (listAccess.ts, listEmitter.ts primitives) |
| 04-03 (listsRouter + ListsIndex + ListCard — prerequisite list data layer) |
|
| POST/GET /api/lists/:id/items with fractional rank (D-13) |
| PATCH /api/list-items/:id per-field LWW (D-08, exactly-one-field zod refine) |
| DELETE /api/list-items/:id delete-wins (D-09) |
| rank.ts |
| rankForAppend + rankBetween (fractional-indexing wrappers) |
|
| ListDetail with active/completed split (D-05), optimistic mutations (D-07/D-09) |
| ItemRow with checkbox, plain-text text, GripVertical slot, swipe/hover delete |
| AddItemInput sticky bottom input |
| listsClient item functions |
| fetchListItems, addItem, patchListItem, deleteItem |
|
|
| apps/api/src/routes/lists.ts (item routes added, listItemsRouter exported) |
| apps/api/src/index.ts (listItemsRouter mounted at /api/list-items) |
| apps/api/src/lib/rank.ts (new) |
| apps/api/tests/lib/rank.test.ts (new) |
| apps/api/tests/routes/lists.test.ts (item route tests added) |
| apps/pwa/src/api/listsClient.ts (item functions added) |
| apps/pwa/src/routes/ListDetail.tsx (placeholder replaced with real implementation) |
| apps/pwa/src/routes/ListDetail.test.tsx (todo stubs replaced with real tests) |
| apps/pwa/src/components/ItemRow.tsx (new) |
| apps/pwa/src/components/AddItemInput.tsx (new) |
|
|
| added |
patterns |
| fractional-indexing (already installed from Plan 04-01) |
|
| rankForAppend wraps generateKeyBetween(lastRank, null) |
| patchItemSchema .partial().refine(exactly one field) for D-08/T-04-07 |
| listItemsRouter separate from listsRouter, mounted at /api/list-items |
| Optimistic mutations |
| onMutate/onError/onSettled against ['list', listId] |
|
| Delete-wins |
| no onError rollback in deleteMutation (D-09) |
|
| Uncheck recomputes rank to active-bottom in same DB write (Open Question 2) |
|
|
| created |
modified |
| apps/api/src/lib/rank.ts |
| apps/api/tests/lib/rank.test.ts |
| apps/pwa/src/components/ItemRow.tsx |
| apps/pwa/src/components/AddItemInput.tsx |
|
| apps/api/src/routes/lists.ts (item routes, listItemsRouter export) |
| apps/api/src/index.ts (listItemsRouter mount) |
| apps/api/tests/routes/lists.test.ts (25 new tests) |
| apps/pwa/src/api/listsClient.ts (item functions + ListItemsResponse type) |
| apps/pwa/src/routes/ListDetail.tsx (placeholder replaced) |
| apps/pwa/src/routes/ListDetail.test.tsx (7 real tests) |
|
|
| listItemsRouter exported separately from listsRouter; mounted at /api/list-items so PATCH/DELETE resolve at /api/list-items/:id per RESEARCH architecture diagram |
| Uncheck rank: recompute to active-bottom (generateKeyBetween(lastActiveRank, null)) in same write per Open Question 2 from 04-RESEARCH.md |
| Optimistic add uses negative id as temporary identifier (item.id < 0 → dim opacity 0.6) |
| Delete-wins: no onError rollback in deleteMutation; onSettled invalidates to reconcile |
| GripVertical drag handle present in ItemRow but non-functional (Plan 05 wires dnd-kit) |
|
| duration |
completed |
task_count |
file_count |
| ~11 minutes |
2026-06-09 |
2 |
10 |
|