0000_easy_slipstream already created lists/list_shares/list_items and the
calendars unique constraint, but 0001_lists_schema re-created those tables and
0001_calendars_user_url_unique was an orphan (not in _journal) — so a cold
`drizzle-kit migrate` against an empty DB failed with ERROR 1050 'Table lists
already exists'. Dev only survived because its DB was built incrementally; CI
is the first cold migrate and exposed it.
Regenerated a single 0000_baseline.sql from schema.ts. Verified on a fresh
mariadb:11: migrate succeeds, schema is structurally identical to the running
dev DB, `drizzle-kit generate` reports no drift, and all 238 API tests pass.
Local dev DBs must be rebuilt (drop + db:migrate); no prod exists.
- schema.ts: new pushSubscriptions mysqlTable (user_id FK cascade, endpoint unique, p256dh, auth)
- schema.ts: add nullable title varchar(500) to calendarEvents after rawVevent (D-02/NOTIF-01)
- 0003_same_xavin.sql: CREATE TABLE push_subscriptions + ALTER calendar_events ADD title
- migration applied to dev DB via db:generate + db:migrate (NOT db:push per anti-pattern)
- docker-compose.yml: inject VAPID_PUBLIC_KEY/PRIVATE_KEY/SUBJECT into api environment block
- .env.example: document all three VAPID vars with placeholders + generation instructions
The 0002_yielding_mattie_franklin.sql migration was committed but its
drizzle-kit journal entry and snapshot were left untracked. Without these,
drizzle-kit cannot track the migration as applied.
BUG A — timed events written 4h off: EventForm sent a naive local wall-clock
string with no offset; the UTC API container parsed it via new Date() as UTC, so
09:00 America/Toronto serialized to DTSTART:...090000Z. Fix: new
apps/pwa/src/lib/eventDateTime.ts serializes timed events to an unambiguous UTC
instant in the browser (where the operator's zone is known); all-day stays a DATE
string. No backend change.
BUG B — created events attached to the wrong user's calendar + duplicate calendar
rows per poll: calendars had no unique key on url, and poller/sync matched
calendars by url alone — so under the shared single Fastmail account (D-16) one
member's collection resolved to the other member's row. Fix: composite
unique(user_id, url); scope poller lookup + sync select to (userId, url); hand
migration 0001 (dedup + add key), applied to the live DB.
Regression tests fail against the buggy url-only predicate. API 98/98, PWA 140/140,
tsc clean both packages.
- Add calendarEvents.hasRrule boolean + idx_calendar_events_has_rrule index (Phase 2 pre-filter)
- Add calendars.isShared boolean for shared-family calendar identification
- Create apps/pwa/vitest.config.ts with jsdom environment
- Add vitest, @testing-library/react, jsdom, @testing-library/jest-dom to PWA devDependencies
- Add "test": "vitest run" script to apps/pwa/package.json
- Create three ICS fixtures: weekly-dst.ics (DST spanning), allday-birthday.ics, exdate-series.ics
- Create RED test stub expand.test.ts with concrete DST wall-clock assertions (10:00 local both sides of March 2026 boundary)
- Create RED test stub events.test.ts with 400 validation and color/isShared field contracts
- Create RED test stub hydrateEvents.test.ts with Temporal type and calendarId routing contracts (shared→"shared", personal→String(ownerUserId))
- Create RED test stub calendarConfig.test.ts with firstDayOfWeek 0→7 translation contract