8.6 KiB
phase, plan, subsystem, tags, dependency_graph, tech_stack, key_files, decisions, metrics
| phase | plan | subsystem | tags | dependency_graph | tech_stack | key_files | decisions | metrics | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 02-calendar-display | 01 | api-schema, api-auth, pwa-test |
|
|
|
|
|
|
Phase 02 Plan 01: Foundation — Schema Columns, Test Harness, Dev-Auth Bypass Summary
Horizontal foundation for Phase 2 calendar slice: two schema columns pushed to live MariaDB, PWA jsdom test runner operational, dev-auth bypass middleware with production hard guard, three ICS fixtures, and four RED test stubs with concrete behavioral contracts.
What Was Built
Schema Changes (Task 1)
Added to apps/api/src/db/schema.ts:
calendarEvents.hasRrule:boolean('has_rrule').default(false).notNull()— pre-filter flag for recurring event masters (RESEARCH.md §Pitfall 5)calendarEvents: new indexidx_calendar_events_has_rrulematching style ofidx_calendar_events_dtstart_utccalendars.isShared:boolean('is_shared').default(false).notNull()— operator-marked shared-family calendar flag
Both columns pushed to live MariaDB (503-event cache intact). SHOW COLUMNS confirms presence.
PWA Test Harness (Task 1)
- Created
apps/pwa/vitest.config.tswithenvironment: 'jsdom'andglobals: true - Added
"test": "vitest run"toapps/pwa/package.jsonscripts - Added devDependencies:
vitest@^4.1.8,@testing-library/react@^16.3.0,@testing-library/jest-dom@^6.6.3,jsdom@^26.1.0 pnpm installcompleted without errors
ICS Fixtures (Task 1)
Three fixtures created at apps/api/tests/fixtures/:
weekly-dst.ics: weekly VEVENT atDTSTART;TZID=America/New_York:20260301T100000with full VTIMEZONE block (STANDARD + DAYLIGHT subcomponents for March 2026 EST→EDT transition)allday-birthday.ics:DTSTART;VALUE=DATE:20260615withRRULE:FREQ=YEARLY, no DTEND — pure DATE typeexdate-series.ics:RRULE:FREQ=WEEKLY;COUNT=5withEXDATE;TZID=America/New_York:20260615T090000— exactly one occurrence excluded
All three fixtures parse via ICAL.parse() without throwing.
RED Test Stubs (Task 1)
Four test stubs with concrete behavioral contracts (not bare failing imports):
expand.test.ts: Three behavioral contracts —
- DST wall-clock: every occurrence in March 2026 window has
T10:00:00in the ISO start string, regardless of EST/EDT offset. Tests both pre-transition (2026-03-01) and post-transition (2026-03-15) occurrences. - All-day:
allDay:trueandstart === '2026-06-15'(noTcomponent) - EXDATE: length === 4 (not 5), June 15 occurrence absent
events.test.ts: Four contracts — 400 on missing start, 400 on missing end, 400 on malformed date, 200 + {occurrences: []} with color/isShared fields on valid window.
hydrateEvents.test.ts: Four contracts — all-day → Temporal.PlainDate, timed → Temporal.ZonedDateTime, shared isShared:true → calendarId 'shared', personal isShared:false ownerUserId:7 calendarId:99 → calendarId '7' (NOT '99').
calendarConfig.test.ts: Four contracts — WEEK_START_DAY === 0, firstDayOfWeek === 7 (Temporal 0→7 translation), 'shared' calendar in config, per-member by String(userId).
All RED stubs fail at import resolution (module not built yet) — correct RED state.
Dev-Auth Bypass (Task 2)
Created apps/api/src/auth/devBypass.ts:
- Exports
devAuthBypass(): MiddlewareHandler - First conditional is
NODE_ENV === 'production'— hard guard (T-02-01 mitigation) - Returns no-op passthrough when production OR bypass flag unset
- When active:
c.set('user', DEV_USER)thenawait next() - Exports
DEV_USER = { id: 1, oidcIss: 'dev', oidcSub: 'dev-user', displayName: 'Dev User', color: COLOR_PALETTE[0] }
Mounted in apps/api/src/index.ts on the line immediately before oidcAuthMiddleware().
All three devBypass.test.ts cases pass: production guard, unset-flag passthrough, active injection.
.env.example and docs/deployment.md updated with bypass documentation and production prohibition.
Deviations from Plan
Auto-fixed Issues
1. [Rule 3 - Blocker] drizzle-kit push replaced with direct ALTER TABLE
- Found during: Task 3
- Issue:
drizzle-kit pushemitted a non-TTY interactive prompt. The "data-loss" warnings were false positives — MariaDB stores int asint(11)display width but drizzle-kit 0.31.x sees this as a type change on existing rows. The prompt cannot be auto-confirmed without TTY. - Fix: Applied the two actual new columns directly via
ALTER TABLE calendar_events ADD COLUMN IF NOT EXISTS has_rrule tinyint(1) NOT NULL DEFAULT 0andALTER TABLE calendars ADD COLUMN IF NOT EXISTS is_shared tinyint(1) NOT NULL DEFAULT 0, plus the index. Outcome is identical to what drizzle-kit push would have done for the new columns. - Data integrity: 503 events confirmed intact post-migration. SHOW COLUMNS confirms both columns and the index exist.
- Note for future plans: The int(11) vs int type drift is a display-width-only issue in MariaDB. It does not affect runtime behavior. If drizzle-kit push is run again, it may continue to prompt about these. Consider adding
drizzle.config.tsoverrides or accepting the prompt in an attended session. - Files modified: live MariaDB schema (no source file change)
Known Stubs
The following test stubs are intentionally RED (modules not yet built):
apps/api/tests/broker/expand.test.ts— awaitsapps/api/src/broker/expand.ts(Plan 02)apps/api/tests/routes/events.test.ts— awaits evolvedapps/api/src/routes/events.ts(Plan 02)apps/pwa/src/lib/hydrateEvents.test.ts— awaitsapps/pwa/src/lib/hydrateEvents.ts(Plan 03)apps/pwa/src/lib/calendarConfig.test.ts— awaitsapps/pwa/src/lib/calendarConfig.ts(Plan 03)
These are tracked RED stubs, not incomplete work. Each encodes a concrete behavioral contract for the implementing plan.
Threat Flags
No new threat surface introduced beyond what is already in the plan's threat model. The devAuthBypass middleware is guarded by both NODE_ENV === 'production' and documented in .env.example and docs/deployment.md.
Self-Check: PASSED
Files created:
- apps/api/src/auth/devBypass.ts — FOUND
- apps/pwa/vitest.config.ts — FOUND
- apps/api/tests/fixtures/weekly-dst.ics — FOUND
- apps/api/tests/fixtures/allday-birthday.ics — FOUND
- apps/api/tests/fixtures/exdate-series.ics — FOUND
- apps/api/tests/broker/expand.test.ts — FOUND
- apps/api/tests/routes/events.test.ts — FOUND
- apps/api/tests/auth/devBypass.test.ts — FOUND
- apps/pwa/src/lib/hydrateEvents.test.ts — FOUND
- apps/pwa/src/lib/calendarConfig.test.ts — FOUND
Commits:
DB state:
- SHOW COLUMNS FROM calendar_events LIKE 'has_rrule' — returns 1 row
- SHOW COLUMNS FROM calendars LIKE 'is_shared' — returns 1 row
- 503 events intact