Lucas Berger
d49c5f1c9c
feat(04-05): dnd-kit drag-to-reorder active items (LIST-03, D-13/D-14/D-15)
...
- ItemRow: useSortable with drag listeners scoped to GripVertical handle only;
CSS transform animation for remote reorders (D-14); grabbing cursor when dragging
- ListDetail: DndContext/SortableContext over active items; PointerSensor (immediate),
TouchSensor (200ms delay + 5px tolerance — no accidental scroll drags),
KeyboardSensor (accessibility fallback)
- onDragEnd: computes generateKeyBetween(prevRank, nextRank) at destination, fires
optimistic setQueryData then PATCHes { position: newRank } — one-row write (D-13)
- Rollback on PATCH error restores previous order via onError (D-15 LWW convergence)
- Completed items receive no drag handle (not reorderable per UI-SPEC)
2026-06-09 13:13:11 -04:00
Lucas Berger
8ed105d467
docs(04-04): complete item-CRUD vertical slice plan
2026-06-09 13:04:48 -04:00
Lucas Berger
6da9c2ae7b
feat(04-04): add ListDetail with active/completed split + ItemRow + AddItemInput (LIST-02)
...
- listsClient.ts: add fetchListItems, addItem, patchListItem, deleteItem + ListItemsResponse type
- ListDetail.tsx: replace placeholder with real implementation — useQuery(['list', listId])
with 30s polling fallback (D-12); active/completed split (D-05); optimistic mutations (D-07);
delete-wins no-rollback (D-09); per-field check PATCH (D-08)
- ItemRow.tsx: 44px touch target, checkbox (20px visual/44px touch, accent fill when checked),
plain-text item text (T-04-06 XSS guard), GripVertical handle slot for Plan 05,
hover Trash2 delete + swipe-left zone, transform 150ms ease-out animation slot (D-14)
- AddItemInput.tsx: sticky bottom input + Add button, disabled when empty, Enter key support
- ListDetail.test.tsx: 7 real tests replacing todo stubs — optimistic add/check/uncheck/delete,
rollback on error, D-05 completed-sink split, D-09 delete-wins no-rollback
- Playwright browser check: add milk → sinks to Completed on check → vanishes on delete PASS
2026-06-09 13:01:49 -04:00
Lucas Berger
5e3151416c
feat(04-04): implement item CRUD endpoints + fractional rank (LIST-02)
...
- Add rank.ts: rankForAppend/rankBetween wrapping fractional-indexing (D-13)
- Extend listsRouter: POST /:id/items (fractional rank at active-bottom),
GET /:id/items (rank ASC, access-gated)
- Add listItemsRouter (mounted /api/list-items): PATCH /:itemId per-field LWW
(exactly-one-field zod refine D-08/T-04-07), DELETE /:itemId delete-wins (D-09)
- Uncheck recomputes rank to active-bottom in same write (Open Question 2)
- All item handlers: access-gate via checkListAccess (T-04-05)
- Plan 06 SSE seam comments at each mutation handler
- All 48 tests green; typecheck passes
2026-06-09 12:55:32 -04:00
Lucas Berger
b1dc9b8048
test(04-04): add failing tests for item CRUD endpoints + rank helpers
...
- Add rank.test.ts: unit tests for rankForAppend/rankBetween (RED - no impl yet)
- Extend lists.test.ts with item route tests: POST /:id/items, GET /:id/items,
PATCH /list-items/:id (per-field LWW D-08), DELETE /list-items/:id (D-09)
- Import listItems from schema; add seedItem helper
- Tests cover: fractional rank assignment (D-13), exact-one-field refine (T-04-07),
uncheck rank recompute, access gating T-04-05, delete-wins no resurrection D-09
2026-06-09 12:52:34 -04:00
Lucas Berger
353431c8b4
docs(04-03): complete list-CRUD vertical slice plan
2026-06-09 12:48:00 -04:00
Lucas Berger
95dbc663c1
feat(04-03): wire ListsIndex + ListCard + CreateListSheet + ListDeleteDialog (LIST-01)
...
- listsClient.ts: add createList/patchList/deleteList + List/ListItem types with activeCount/doneCount
- ListsEmptyState.tsx: extracted standalone component (ClipboardList icon, UI-SPEC copy)
- ListCard.tsx: name/count badge/Shared pill/ChevronRight; hover-reveal delete button; navigates /lists/:id
- CreateListSheet.tsx: bottom-sheet/modal; Shared default (D-01); optimistic useMutation; auto-focus; Escape to close
- ListDeleteDialog.tsx: mirrors DeleteConfirmationDialog pattern; props-driven (no calendarStore); XSS guard on name
- ListsIndex.tsx: replaced placeholder with real data via useQuery+useMutation; mounts CreateListSheet+ListDeleteDialog
- DeleteConfirmationDialog.tsx NOT modified (stable, D-06 pattern preserved)
- PWA typecheck passes; DeleteConfirmationDialog.test.tsx 10 passed
- Playwright E2E: create Groceries+Gift Ideas (Shared pills); delete dialog → confirm → card disappears
2026-06-09 12:45:19 -04:00
Lucas Berger
9546b747d2
feat(04-03): implement listsRouter POST/GET/PATCH/DELETE /api/lists (LIST-01)
...
- GET /: scoped access (owner + list_shares); activeCount/doneCount per list
- POST /: auto-populates list_shares for all other members when isShared=true (D-01/D-02)
- PATCH /🆔 rename + isShared toggle; reconciles list_shares on visibility change
- DELETE /🆔 owner-only; cascade handles items/shares via FK onDelete cascade
- resolveUserId helper copied verbatim from events.ts per project convention
- zod createListSchema (name 1..255, isShared default true) + patchListSchema
- T-04-02 / T-04-05 / T-04-07 / T-04-08 mitigations applied
- listsRouter mounted at /api/lists in index.ts (after sseRouter)
- Plan 06 SSE seam comments left at every mutation handler
- [Rule 1 - Fix] zValidator returns 400 (not 422); tests corrected to match convention
- All 23 tests green; full API suite 140 passed no regressions
2026-06-09 12:38:05 -04:00
Lucas Berger
2b3d7896f1
test(04-03): add failing integration tests for lists router (LIST-01, D-01/D-02/D-04/D-06)
...
- GET /api/lists scoped access tests (empty, owned, shared, D-04 negative)
- GET /api/lists item count (activeCount/doneCount) assertion
- POST /api/lists shared/private create + auto list_shares + zod validation
- DELETE /api/lists/:id owner/403/404/cascade tests
- PATCH /api/lists/:id rename/share toggle/403/zod tests
- All fail 404 (router not yet mounted) — RED gate confirmed
2026-06-09 12:34:49 -04:00
Lucas Berger
9fb1e0da84
docs(04-02): complete scoped fan-out primitives plan
...
SUMMARY.md for 04-02: listEmitter + listAccess TDD plan.
RED/GREEN gate confirmed. D-04 isolation proven. ioredis not introduced.
2026-06-09 12:24:50 -04:00
Lucas Berger
9e17853d89
fix(04-02): use fileParallelism:false to prevent DB test race conditions
...
Replaced singleFork:true + sequence config with the simpler fileParallelism:false
which correctly serializes test file execution. The previous singleFork approach
ran tests from multiple files concurrently within one process, allowing the global
afterEach cleanup (test/setup.ts: truncates lists/listShares) to delete rows mid-test
in another file, causing intermittent FK violations (ER_DUP_ENTRY, ER_NO_REFERENCED_ROW).
fileParallelism:false runs one test file at a time so afterEach cleanup for file A
never races with insertions from file B.
2026-06-09 12:23:13 -04:00
Lucas Berger
792efeb3df
feat(04-02): implement listEmitter + listAccess; all 9 tests GREEN
...
listEmitter.ts:
- Module-level EventEmitter singleton; setMaxListeners(200) (T-04-04)
- publishListEvent(listId, event): emits on list:${listId} channel
- subscribeListEvents(listId, handler): registers listener, returns unsub closure
- ListEvent type union: item:added/updated/deleted, list:updated/deleted
- D-04 isolation guaranteed by per-list channel keying
listAccess.ts:
- getAccessibleListIds(userId): two SELECT queries (owned + shared), Set dedupe
- Satisfies T-04-02/T-04-03: over-returning proven impossible by Test 7
listAccess.test.ts fix:
- Use randomUUID() suffix in seedUser to avoid oidc_sub unique-key collisions
across test re-runs (users table not truncated by global afterEach)
vitest.config.ts:
- pool: 'forks' + singleFork: true to prevent FK violations from concurrent
DB workers racing against the shared-state global afterEach cleanup
- sequence.concurrent: false as belt-and-suspenders
ioredis NOT introduced (D-18 abstraction boundary satisfied)
2026-06-09 12:20:38 -04:00
Lucas Berger
2d250afce2
test(04-02): add failing listEmitter + listAccess tests (RED gate)
...
- listEmitter.test.ts: 5 real assertions replacing it.todo stubs
(Tests 1-4: scoped delivery, D-04 negative cross-list isolation, unsub, multi-handler, D-18 scale)
- listAccess.test.ts: 4 DB-backed assertions (Tests 5-8)
(owned lists, shared via list_shares, D-04 negative private exclusion, dedupe)
- Both files fail: listEmitter.ts and listAccess.ts do not exist yet
2026-06-09 12:15:19 -04:00
Lucas Berger
60745b3281
refactor(04-01): move API list test stubs into tests/ mirror dir to match convention
...
The two Wave-0 RED stubs (lists.test.ts, listEmitter.test.ts) were co-located in
src/ but all existing API tests live in apps/api/tests/. Move them to tests/routes/
and tests/lib/, add explicit vitest imports to match the tests/ convention, and
update path references in downstream plans 04-02..04-06. PWA tests keep co-location
(that IS the PWA convention).
2026-06-09 12:10:31 -04:00
Lucas Berger
0fd4d66ee7
docs(04-01): complete lists foundation plan
2026-06-09 12:06:53 -04:00
Lucas Berger
c0088edf44
feat(04-01): wire BrowserRouter + BottomTabBar + empty Lists surface
...
- App.tsx: BrowserRouter with /calendar, /lists, /lists/:listId routes; / redirects to /calendar
- BottomTabBar.tsx: fixed-bottom 56px tab bar with Calendar + Lists NavLinks, active accent
- AppNav.tsx: add Calendar/Lists NavLinks to desktop sidebar (≥768px)
- ListsIndex.tsx: full-height surface with isLoading/isError/empty state branches; "+ New List" FAB placeholder
- ListDetail.tsx: placeholder stub for /lists/:listId (Plan 04-04 fills in)
- listsStore.ts: Zustand UI-only store (activeTab, createListSheetOpen)
- listsClient.ts: fetchLists + List/ListItem types (initial; Plans 04-02/03 expand)
- Fix Wave-0 RED stubs: add vitest imports so stubs execute (todo) not error on import
- Fix CalendarShell.test.tsx: wrap renderWithClient in MemoryRouter (AppNav uses NavLink)
2026-06-09 12:03:52 -04:00
Lucas Berger
2f25b15949
feat(04-01): add list tables to schema and apply via generate+migrate [BLOCKING]
...
- Append lists, list_shares, list_items tables to Drizzle schema (schema.ts)
- lists: owner_id FK, is_shared bool default true (D-01), idx_lists_owner_id
- list_shares: list_id + user_id FKs, uniq_list_share, idx_list_shares_user_id (D-02)
- list_items: rank varchar for fractional-indexing (D-13), checked bool, composite indexes
- Generate 0000_easy_slipstream.sql (full schema baseline) + 0001_lists_schema.sql (new tables)
- Mark 0000 as applied in __drizzle_migrations (prior tables existed from manual DDL)
- Apply 0001_lists_schema.sql via db:migrate — lists/list_shares/list_items now in MariaDB
- Add vitest/globals + node to tsconfig types for test file compatibility
- NEVER used db:push (hard project constraint — drizzle-mariadb-push-unsafe)
- typecheck passes
2026-06-09 11:55:46 -04:00
Lucas Berger
39d4ec84c0
feat(04-01): install new deps + scaffold API test harness with Wave-0 RED stubs
...
- Add react-router@7, @dnd-kit/core, @dnd-kit/sortable, fractional-indexing to PWA
- Add fractional-indexing to API (rank generation server-side)
- ioredis NOT added (in-memory EventEmitter per RESEARCH Plan 02 justification)
- Create apps/api/test/setup.ts with afterEach DB cleanup for list tables
- Wire test.setupFiles in apps/api/vitest.config.ts
- Add 4 Wave-0 RED stub test files (LIST-01/02/03/04, D-04, D-11, D-07)
- All stubs run as todo, not import-error
2026-06-09 11:49:10 -04:00
Lucas Berger
95b13e6633
chore(04): commit pattern map + session config; ignore intel/graph diff baselines
2026-06-09 11:40:19 -04:00
Lucas Berger
eb8ae15862
docs(03): add security threat verification (25/25 closed)
2026-06-09 11:38:53 -04:00
Lucas Berger
2a093468a4
chore: build intel index (.planning/intel/)
2026-06-09 11:33:33 -04:00
Lucas Berger
a9da31cc35
test(03): complete UAT - 10 passed, 0 issues
2026-06-09 11:31:12 -04:00
Lucas Berger
01f7456b81
chore: add knowledge graph + ignore graphify-out build cache
2026-06-09 11:25:46 -04:00
Lucas Berger
00cbbb41a6
docs: map existing codebase
2026-06-09 11:20:11 -04:00
Lucas Berger
7c687ea413
docs(03): add code review fix report (--auto, 2 fix passes)
...
Auto-fix loop converged after 2 fix iterations + a final verifying re-review:
- Pass 1: 13/14 findings fixed (3 Critical, 6 Warning, 4 Info).
- Re-review surfaced 1 new Critical (move-path RRULE data loss) + 4 lower.
- Pass 2: 8/8 fixed, including the move-path RRULE forwarding.
- Final re-review: 0 Critical. Remaining 2 Warning / 2 Info are documented
v1 scope cuts (recurrence-editing deferred), not defects.
Test suites green throughout: api 108, pwa 145; both tsc --noEmit clean.
Per-iteration REVIEW/REVIEW-FIX snapshots retained as audit trail.
2026-06-09 11:12:09 -04:00
Lucas Berger
b8c186491b
fix(03): IN-03 re-validate outbox payload before VEVENT build, hard-fail invalid rows
2026-06-09 11:05:50 -04:00
Lucas Berger
f95760e6c6
fix(03): IN-01 document RRULE_PRESETS lossy round-trip v1 limitation
2026-06-09 11:05:50 -04:00
Lucas Berger
fd13852eb9
fix(03): WR-04 rank failed/dead outbox row above done in sync-status
2026-06-09 11:04:00 -04:00
Lucas Berger
5b720ffdb8
fix(03): WR-03 log unconditional PUT/DELETE when If-Match etag is missing
2026-06-09 11:04:00 -04:00
Lucas Berger
eed178fb39
fix(03): WR-01/WR-02/IN-02 recurrence-edit helper text, all-day toggle clamp, edit-mode parse-failure guard
2026-06-09 11:02:09 -04:00
Lucas Berger
5168920eb1
fix(03): CR-01 preserve RRULE on edit-as-move (forward source rule to create row)
2026-06-09 10:59:19 -04:00
Lucas Berger
7a48659cae
fix(03): update event lookup test mocks for CR-01/CR-02 query-chain changes
...
The CR-01 fix appended .orderBy().limit(1) to the edit/delete event lookups
and CR-02 added .innerJoin(calendars).limit(1) to the freshest-etag re-read.
The existing test doubles terminated the mock chain at .where(), so the new
chain calls hit undefined methods → handlers caught the throw and returned 503
(events.test.ts) and the worker skipped the PUT (outboxWorker.test.ts).
Extend the mocks to match the corrected production chains. Behaviour-preserving:
mockWhereCalEvents stays the awaited terminal so etag override assertions still drive.
8 failing tests now green; full suite: api 103, pwa 141.
2026-06-09 10:51:10 -04:00
Lucas Berger
6d2fd79209
fix(03): IN-04 type resolveUserId param as Hono Context instead of any
2026-06-09 10:44:15 -04:00
Lucas Berger
7e4ea710d0
fix(03): IN-03 guard localStorage access in InstallPrompt
2026-06-09 10:43:37 -04:00
Lucas Berger
e29d6c1714
fix(03): IN-02 treat unmapped 4xx as hard fail (no full-backoff retry)
2026-06-09 10:43:14 -04:00
Lucas Berger
95f9d8c097
fix(03): IN-01 cache decrypted client per userId within a drain cycle
2026-06-09 10:42:41 -04:00
Lucas Berger
1c71f8c980
fix(03): WR-06 surface move-failed distinctly with re-save guidance
2026-06-09 10:41:55 -04:00
Lucas Berger
7bc129f0f3
fix(03): WR-04 start background workers only when run as entrypoint
2026-06-09 10:40:54 -04:00
Lucas Berger
22d1bc27d6
fix(03): WR-05 use resolved realpath for direct-run guard
2026-06-09 10:40:26 -04:00
Lucas Berger
d34edece96
fix(03): WR-03 use cleaned string for all-day check in parseDateTime
2026-06-09 10:39:54 -04:00
Lucas Berger
5499f83782
fix(03): WR-02 make default-calendar selection deterministic (orderBy id, limit 1)
2026-06-09 10:39:28 -04:00
Lucas Berger
02aa407764
fix(03): WR-01 preserve existing RRULE on edit instead of resetting to none
2026-06-09 10:39:06 -04:00
Lucas Berger
f645644853
fix(03): CR-03 convert exclusive all-day end to inclusive on edit pre-fill
2026-06-09 10:37:13 -04:00
Lucas Berger
a596f520b4
fix(03): CR-02 scope freshest-etag re-read to the writing member's calendar
2026-06-09 10:36:18 -04:00
Lucas Berger
54addb1515
fix(03): CR-01 scope event edit/delete lookup to acting member's writable set
2026-06-09 10:35:51 -04:00
Lucas Berger
8b519460d9
docs(03): add code review report
2026-06-09 10:32:20 -04:00
Lucas Berger
197e138e3b
docs(04): finalize phase plan (coverage gates + roadmap annotations)
2026-06-09 10:25:22 -04:00
Lucas Berger and Claude Opus 4.8
38fa6f448b
docs(04): create phase plan — 6 plans across 5 waves for shared lists + live sync
...
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-09 10:21:53 -04:00
Lucas Berger
d89eb47483
docs(03): add code review report
2026-06-09 10:19:01 -04:00
Lucas Berger
a348bdd815
docs(04): add validation strategy
2026-06-09 10:03:12 -04:00