- timed event with reminderLeadMinutes=10080 must show 'Custom reminder kept' helper
- currently suppressed: helper text checks !TIMED && !ALLDAY, but 10080 is in ALLDAY
- fix: gate helper text on active preset set only (allDay ? ALLDAY : TIMED)
- expand.ts: add reminderIsCustom:boolean to CalendarOccurrence interface;
derived from classifyValarms kind==='custom'; propagated to both
non-recurring and recurring occurrence branches
- client.ts: mirror reminderIsCustom on CalendarOccurrence (atomic mirror)
- EventForm.tsx: extend deriveReminderValue to accept isCustom flag;
returns '__custom__' when true, making the existing D-08 preserve branch
live — editing a custom-alarm event now omits reminderLeadMinutes from
the payload so outboxWorker extractValarms keeps the original VALARM
- Fix existing test fixtures (EventForm.test.tsx, EventDetailPopover.test.tsx)
to include reminderIsCustom:false on all CalendarOccurrence literals
Fixes CAL-14 Pitfall 1: Apple Calendar absolute DATE-TIME / multi-VALARM
alarms no longer silently stripped on any edit round-trip from the PWA.
- expand.test.ts: 3 new tests asserting reminderIsCustom:true for
absolute DATE-TIME trigger and multi-VALARM, false for relative preset
- EventForm.test.tsx: 3 new tests asserting __custom__ picker init,
'Custom (kept)' option visibility, and payload omits reminderLeadMinutes
- Fixtures: absolute-alarm.ics (DATE-TIME VALARM), multi-alarm.ics (2 VALARMs)
- All 6 new tests FAIL (RED): reminderIsCustom field not yet on interface
- CalendarOccurrence: required reminderLeadMinutes: number | null (atomic mirror of expand.ts, Plan 11-03)
- CreateEventPayload: optional reminderLeadMinutes?: number | null with absent/null/0/positive contract (D-08)
- Update CalendarOccurrence fixtures in EventForm.test.tsx + EventDetailPopover.test.tsx to include the new required field (reminderLeadMinutes: null)
- pwa tsc --noEmit exits 0
- eslint.config.js: disable React Compiler rules (v7 flat.recommended enables
them; codebase does not use the Compiler); add e2e/ to disableTypeChecked
block; promote exhaustive-deps to error
- API broker: remove redundant as-casts (outboxWorker, poller, reminderScheduler,
expand, sync, vevent, spike); add targeted ical.js no-unsafe-assignment/argument
disables with justifying comments inside try blocks
- API routes/sse.ts: fix no-misused-promises on async writeSSE callback with
void+IIFE+catch pattern
- API routes/lists.ts: let → const for updateValues
- API tests: remove unused imports (beforeEach, eq, vi); rename unused vars
with _ prefix; remove unused lastActiveId assignment
- PWA components: void navigate() and void queryClient.invalidateQueries() on
all fire-and-forget call sites; fix CalendarShell explicit-type-casts;
Couldn't → HTML entity
- PWA test files: as unknown as Response for partial mock objects; string | null
type annotation on mockLastSyncedUid; remove async from test callbacks without
await; act(() => {}) not await act(async () => {}) for sync ops
- sw.ts: restructure Notification.data?.url access as let+if so disable
comments land on the exact violation lines; void self.skipWaiting()
- Add hasRrule to EDIT_OCCURRENCE, RECURRING_OCCURRENCE, LATE_OCCURRENCE fixtures
- Add ALL_DAY_OCCURRENCE fixture for D-05 round-trip test
- Add test cases: D-04 timed/all-day end-tracking on start change
- Add test cases: D-05 all-day edit pre-fills inclusive end (no drift)
- Add test cases: D-06 Ends control visibility, On date/After N times reveals
- Add test cases: D-06 validation (count < 1), payload emission (count, never)
Add two failing tests for the focus trap:
- Tab from last focusable element must wrap to first inside dialog
- Shift+Tab from first focusable element must wrap to last inside dialog
Both fail today because EventForm only calls .focus() once on open;
Tab escapes the modal to background content.
WR-03 blank: add occurrence?.uid to reset effect deps so form re-populates
when occurrence resolves in TanStack cache after form opens.
WR-03 recurrence: derive initial recurrence from occurrence?.recurrence
instead of hard-coding 'none'; defaults to 'none' when absent (v1 comment).
WR-05: rewrite parseDateTime to use getFullYear/getMonth/getDate/getHours/
getMinutes (all local accessors) — never mix toISOString() UTC date with
getHours() local time.
IN-03: export todayIso from calendarStore (was private); import into EventForm
and collapse getDefaultStartDate/getDefaultEndDate to todayIso() calls.
- WR-03 blank: assert title re-populates when occurrence arrives in TanStack cache after form opens (fails: reset effect ignores occurrence in deps)
- WR-03 recurrence: assert weekly recurring event preselects 'weekly' not 'none' (fails: reset effect hard-codes 'none')
- IN-03: assert todayIso is exported from calendarStore (fails: currently private)
- WR-05: zone-consistent parseDateTime test with TZ=UTC pinned in vitest.config.ts env block
- Pin TZ=UTC in vitest.config.ts for deterministic date-extraction assertions