style(13-03): apply Prettier formatting across repo

Mechanical reformat — no logic changes. 398 files changed, 19125
insertions(+), 16457 deletions(-). Prettier 3.8.4 with .prettierrc
(singleQuote:true, semi:true, tabWidth:2, trailingComma:all,
printWidth:100). Isolated per D-13-08 for reviewability.
This commit is contained in:
Lucas Berger
2026-06-11 20:35:18 -04:00
parent 4bc0445173
commit 982438dc10
398 changed files with 19050 additions and 16382 deletions
+16 -60
View File
@@ -17,103 +17,67 @@
"write-broker-boundary": {
"title": "Write-broker boundary invariant",
"decision": "No route file imports tsdav or createFastmailClient; no broker file handles HTTP requests. Routes enqueue calendar_outbox rows and return 202 (optimistic-accept); the outbox worker performs the Fastmail write asynchronously.",
"files": [
"apps/api/src/routes/events.ts",
"apps/api/src/broker/write.ts"
]
"files": ["apps/api/src/routes/events.ts", "apps/api/src/broker/write.ts"]
},
"identity-keying": {
"title": "Identity keyed on oidc_iss + oidc_sub",
"decision": "Users are keyed on oidc_iss + oidc_sub (never email). A hex color from the palette is auto-assigned on first login.",
"files": [
"apps/api/src/routes/me.ts",
"apps/api/src/auth/middleware.ts"
]
"files": ["apps/api/src/routes/me.ts", "apps/api/src/auth/middleware.ts"]
},
"D-03-writable-set": {
"title": "D-03 calendar ownership / writable-set predicate",
"decision": "Every writable-set query uses WHERE userId = currentUser.id OR isShared = true. Another member's personal calendar is a read-only overlay.",
"files": [
"apps/api/src/routes/events.ts"
]
"files": ["apps/api/src/routes/events.ts"]
},
"D-13-dual-field-dtstart": {
"title": "D-13 all-day vs timed events (dual dtstart fields)",
"decision": "dtstart_utc is NULL for all-day events; dtstart_date is NULL for timed events. Never coerce DATE to DATETIME.",
"files": [
"apps/api/src/db/schema.ts"
]
"files": ["apps/api/src/db/schema.ts"]
},
"D-16-shared-fastmail-account": {
"title": "D-16 shared Fastmail account, per-member credentials",
"decision": "Both members share one Fastmail account. Calendar identity in DB is (userId, url) — the same collection URL appears once per member credential. CalDAV credential per member is stored AES-256-GCM encrypted in member_credentials.",
"files": [
"apps/api/src/db/schema.ts",
"apps/api/src/broker/poller.ts"
]
"files": ["apps/api/src/db/schema.ts", "apps/api/src/broker/poller.ts"]
},
"outbox-status-machine": {
"title": "Outbox status machine",
"decision": "calendar_outbox rows transition pending -> done | failed | dead. failed rows retry up to a limit; dead is terminal. The sync-status endpoint surfaces worst-status-first per uid.",
"files": [
"apps/api/src/broker/outboxWorker.ts",
"apps/api/src/routes/events.ts"
]
"files": ["apps/api/src/broker/outboxWorker.ts", "apps/api/src/routes/events.ts"]
},
"oidc-behind-pangolin": {
"title": "OIDC behind Pangolin requires OIDC_AUTH_EXTERNAL_URL",
"decision": "OIDC_AUTH_EXTERNAL_URL must be set to the public HTTPS URL to construct a correct redirect_uri; without it the callback resolves to the internal container address.",
"files": [
"apps/api/src/auth/middleware.ts",
"apps/api/src/index.ts"
]
"files": ["apps/api/src/auth/middleware.ts", "apps/api/src/index.ts"]
},
"dev-auth-bypass": {
"title": "Dev auth bypass",
"decision": "DEV_AUTH_BYPASS=true with NODE_ENV!=production injects DEV_USER via Hono context; OIDC middleware is never mounted in this mode.",
"files": [
"apps/api/src/auth/devBypass.js",
"apps/api/src/index.ts"
]
"files": ["apps/api/src/auth/devBypass.js", "apps/api/src/index.ts"]
},
"pwa-static-serving": {
"title": "PWA static serving + SPA fallback",
"decision": "Hono serveStatic serves ./public (Vite build output); SPA routes fall through to an index.html catch-all registered after /health, /api/*, and /callback so those win.",
"files": [
"apps/api/src/index.ts"
]
"files": ["apps/api/src/index.ts"]
},
"schedule-x-routing": {
"title": "Schedule-X calendar routing",
"decision": "Events are routed to Schedule-X calendars by isShared ? 'shared' : String(ownerUserId) — never by calendarId. hydrateEvents.ts enforces this.",
"files": [
"apps/pwa/src/lib/hydrateEvents.ts",
"apps/pwa/src/components/CalendarShell.tsx"
]
"files": ["apps/pwa/src/lib/hydrateEvents.ts", "apps/pwa/src/components/CalendarShell.tsx"]
},
"state-ownership": {
"title": "Client state ownership split",
"decision": "Server state is owned by TanStack Query; UI-only state (selected range, color map, drawer) by Zustand. Schedule-X renders the calendar UI.",
"files": [
"apps/pwa/src/store/calendarStore.ts",
"apps/pwa/src/components/CalendarShell.tsx"
]
"files": ["apps/pwa/src/store/calendarStore.ts", "apps/pwa/src/components/CalendarShell.tsx"]
},
"lists-storage-mariadb-not-caldav": {
"title": "Lists stored in MariaDB, not CalDAV (Phase 4)",
"decision": "Named lists and items are app-owned data in MariaDB (lists, list_items, list_shares tables), not pushed to Fastmail. CalDAV is exclusively for calendar events.",
"files": [
"apps/api/src/db/schema.ts",
"apps/api/src/routes/lists.ts"
]
"files": ["apps/api/src/db/schema.ts", "apps/api/src/routes/lists.ts"]
},
"D-01-D-02-list-sharing": {
"title": "D-01/D-02 list sharing via join table (member-count-agnostic)",
"decision": "isShared=true (default) triggers auto-insert of list_shares rows for all other users at create/patch time. Shares are server-managed only — no client-writable shares endpoint (T-04-08). list_shares join table is member-count-agnostic for future N-member expansion.",
"files": [
"apps/api/src/routes/lists.ts",
"apps/api/src/db/schema.ts"
]
"files": ["apps/api/src/routes/lists.ts", "apps/api/src/db/schema.ts"]
},
"D-04-scoped-sse-fan-out": {
"title": "D-04 scoped SSE fan-out — per-list channels, not global",
@@ -127,9 +91,7 @@
"D-08-per-field-lww-patch": {
"title": "D-08 per-field last-write-wins PATCH for list items",
"decision": "PATCH /api/list-items/:itemId accepts exactly one field (checked | text | position). Zod enforces single-field constraint. Prevents one client's stale read overwriting concurrent updates to other fields.",
"files": [
"apps/api/src/routes/lists.ts"
]
"files": ["apps/api/src/routes/lists.ts"]
},
"D-13-fractional-rank": {
"title": "D-13 fractional-indexing rank for list item ordering",
@@ -143,18 +105,12 @@
"D-10-D-11-D-12-sse-resilience": {
"title": "D-10/D-11/D-12 SSE resilience: invalidate-not-patch, bounded backoff, polling fallback",
"decision": "D-10: SSE events carry minimal { type, listId } payload; client full-refetches via TanStack Query invalidation rather than patching cache from event payload. D-11: useListSSE implements bounded backoff (250ms→8s cap, MAX_ATTEMPTS then give-up). D-12: 30s polling fallback always active in ListDetail as safety net.",
"files": [
"apps/pwa/src/hooks/useListSSE.ts",
"apps/pwa/src/routes/ListDetail.tsx"
]
"files": ["apps/pwa/src/hooks/useListSSE.ts", "apps/pwa/src/routes/ListDetail.tsx"]
},
"react-router-spa-shell": {
"title": "react-router BrowserRouter SPA shell with BottomTabBar",
"decision": "App.tsx wraps routes in BrowserRouter with declarative Routes. BottomTabBar is a sibling of Routes (not inside) so it persists across navigation. SW navigateFallback covers /lists/* deep-links.",
"files": [
"apps/pwa/src/App.tsx",
"apps/pwa/src/components/BottomTabBar.tsx"
]
"files": ["apps/pwa/src/App.tsx", "apps/pwa/src/components/BottomTabBar.tsx"]
}
}
}