The _build_member_response helper tried to access member.user.preferred_name
but User model has no preferred_name field (it's on Settings). With lazy="raise"
this caused a 500 on GET /shared-calendars/{id}/members. Reverted to None —
the list_members endpoint already patches preferred_name from Settings.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
W-03: invite_member now verifies the target user has a reciprocal
UserConnection row before sending the invite.
W-04: CalendarMember relationships changed from lazy="selectin" to
lazy="raise". All queries that access .user, .calendar, or .inviter
already use explicit selectinload() — verified across all routers
and services.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Critical:
- C-01: Populate member_count in GET /calendars for shared calendars
- C-02: Differentiate 423 lock errors in drag-drop onError (show lock-specific toast)
- C-03: Add expired lock purge to APScheduler housekeeping job
Warnings:
- W-01: Replace setattr loop with explicit field assignment in update_member
- W-02: Cap sync `since` param to 7 days to prevent unbounded scans
- W-05: Remove cosmetic isShared toggle (is_shared is auto-managed by invite flow)
- W-06: Populate preferred_name in _build_member_response from user model
- W-07: Add releaseMutation to release callback dependency array
Suggestion:
- S-06: Remove unused ConvertToSharedRequest schema
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
SC-01: unlock_event now verifies caller has access to the calendar before
revealing lock state. Previously any authenticated user could probe event
existence via 404/204/403 response differences.
SC-02: acquire_lock no longer overwrites permanent locks. If the owner holds
a permanent lock and clicks Edit, the existing lock is returned as-is instead
of being downgraded to a 5-minute temporary lock.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>