docs(03-09): complete plan 09 summary — schema rename + OIDC resolution

CR-01: eventFieldsSchema aligned to canonical title/start/end (PWA contract)
CR-06: resolveUserId async with upsertUser on all 5 write handlers
19 tests pass; tsc clean
This commit is contained in:
Lucas Berger
2026-06-05 20:42:13 -04:00
parent fac3a21332
commit 0d3883ee02
@@ -0,0 +1,93 @@
---
phase: 03-event-write-back-pwa-install
plan: "09"
subsystem: api-events-router
tags: [tdd, gap-closure, auth, schema, zod, oidc]
dependency_graph:
requires: []
provides:
- canonical-event-schema-title-start-end
- async-resolveUserId-with-upsertUser
affects:
- apps/api/src/routes/events.ts
- apps/api/tests/routes/events.test.ts
- plan-03-10 (outbox worker reads title/start/end from payload)
tech_stack:
added: []
patterns:
- "TDD RED→GREEN per task"
- "vi.hoisted() for configurable per-test auth mocks"
- "async resolveUserId with upsertUser for OIDC path"
key_files:
modified:
- apps/api/src/routes/events.ts
- apps/api/tests/routes/events.test.ts
decisions:
- "D-CR01: Server adopts client field names title/start/end — one canonical name set end-to-end, no rename map"
- "D-CR06: resolveUserId async; dev-bypass path unchanged; OIDC path calls upsertUser(iss,sub,email)"
metrics:
duration_minutes: 6
completed_date: "2026-06-06"
tasks_completed: 2
files_modified: 2
---
# Phase 03 Plan 09: Route Schema + OIDC Resolution Fix Summary
Fix the events router's two blockers that made the write path dead on arrival: align the server zod schema to the PWA's `CreateEventPayload` shape (title/start/end), and implement real OIDC iss/sub → users.id resolution on all five write handlers via `upsertUser`.
## Tasks Completed
| Task | Name | Commit | Files |
|------|------|--------|-------|
| 1 RED | Add contract tests for canonical title/start/end | 944693f | events.test.ts |
| 1 GREEN | Rename eventFieldsSchema to title/start/end (CR-01) | 99cb169 | events.ts, events.test.ts |
| 2 RED | Add OIDC path tests — resolveUserId must call upsertUser | 6d1d338 | events.test.ts |
| 2 GREEN | Async resolveUserId with upsertUser on all 5 handlers (CR-06) | fac3a21 | events.ts |
## Verification
- `cd apps/api && npx vitest run tests/routes/events.test.ts`: 19 tests pass
- `npx tsc --noEmit` in apps/api: clean (no errors)
- `grep -n 'summary\|dtstart\|dtend' eventFieldsSchema`: CLEAN (no old names)
- `grep -c 'For now return 401' events.ts`: 0 stubs remain
- `grep -c 'upsertUser' events.ts`: 3 (import + call in resolveUserId)
## Decisions Made
- **D-CR01**: Server adopts client field names `title/start/end`. No internal rename map — one canonical name set end-to-end from PWA through events router to calendarOutbox payload to outbox worker (plan 03-10).
- **D-CR06**: `resolveUserId` is now async. Dev-bypass path (`c.get('user')`) is unchanged. Production OIDC path calls `getAuth(c)` then `upsertUser(iss, sub, email)` to resolve DB user id. Returns null only when no session exists.
## Deviations from Plan
### Auto-fixed Issues
None. The plan was executed exactly as written, including updating the three existing write tests that previously used the old field names (`summary/dtstart/dtend`) — this was the correct fix since those tests were testing against the wrong boundary (as the review noted).
### Test Infrastructure Deviation (Rule 3)
The worktree has no `node_modules` — the pnpm workspace installs them in the main repo. Created a symlink `apps/api/node_modules → /home/luc/Projects/familysync/apps/api/node_modules` so vitest could run from within the worktree. This is a standard git-worktree-with-pnpm-workspace setup requirement.
## TDD Gate Compliance
Both tasks followed RED→GREEN strictly:
- Task 1: `test(03-09)` commit (944693f) → `feat(03-09)` commit (99cb169)
- Task 2: `test(03-09)` commit (6d1d338) → `feat(03-09)` commit (fac3a21)
## Known Stubs
None. All changes are functional code.
## Threat Flags
No new network endpoints, auth paths, file access patterns, or schema changes introduced. The resolveUserId change closes a security gap (CR-06) by ensuring unauthenticated requests correctly 401 while authenticated OIDC sessions get through.
## Self-Check: PASSED
- events.ts: FOUND
- events.test.ts: FOUND
- 03-09-SUMMARY.md: FOUND
- 944693f (test RED task1): FOUND
- 99cb169 (feat GREEN task1): FOUND
- 6d1d338 (test RED task2): FOUND
- fac3a21 (feat GREEN task2): FOUND