← eventa overview

eventa — Implementation Plan

9 phases, ~62 tasks. Backend first; mobile follows. Real shared state by phase 7, push by phase 8.

eventa — Implementation Plan (Multi-User V1)

A sequenced, sized to-do list to build the multi-user MVP: Python backend with WebSockets + FCM, Android client that talks to it. Distilled from codex rounds 5 and 6.

Sizing: each task is half-a-day to 2 days. Order: dependency first. Phases end with a demo-able outcome.

The plan now has TWO tracks — backend and mobile — that have to be developed somewhat together. The phases below interleave them. By the end of phase 4 you have a single user editing real shared state on the server. By the end of phase 6 you have multi-user editing in real-time. Phase 7 layers push notifications on top.


Phase 1 — Backend skeleton

Outcome: FastAPI service runs locally, has a Postgres DB, has a migration tool, can serve /health. Deployable to tiny1 via the existing deploy/ scaffold.

# Task Done when…
1.1 Scaffold FastAPI + uvicorn project App runs locally and serves /health 200.
1.2 Containerize Postgres for tiny1 dedicated eventa-db postgres-16-alpine container modelled on the troostwijk-scraping pg block, named volume, 127.0.0.1 only.
1.3 Wire SQLAlchemy 2.x + Alembic alembic upgrade head creates a tracked schema; migration #1 just creates users.
1.4 Pydantic settings + .env loader DB DSN, postmark API key, FCM project ID etc. all read from env.
1.5 gunicorn + uvicorn-workers entrypoint start.sh runs the prod-ish stack: gunicorn-uvicorn worker count = 2.
1.6 Deploy to tiny1 via existing scaffold deploy/projects/eventa-api.json + tiny1 deploy eventa-api lands at api.eventa.heapzilla.eu, /health 200 over https.

Phase 2 — Identity + auth

Outcome: a real user can request a magic link, click it, get a bearer token, hit /me. No client yet — curl is enough.

# Task Done when…
2.1 users schema + DAO migration adds users (id, email unique, display_name, last_seen_at, ...).
2.2 magic_links schema + creation API POST /auth/magic-link {email} inserts a token row, expires_at = +30min.
2.3 Resend integration magic-link email sends successfully (test address); template branded as eventa.
2.4 POST /auth/verify exchange token → bearer (JWT or opaque, persisted in bearer_tokens); 410 if consumed/expired.
2.5 Bearer auth middleware every protected endpoint validates bearer; injects current_user.
2.6 GET /me returns user + memberships shape.

Phase 3 — Events + invites + memberships

Outcome: Alex can create an event, invite Gerda, Gerda can accept the invite and now both see the event in /me.

# Task Done when…
3.1 events + event_memberships schemas tables, FKs, version column on events, owner role on creation.
3.2 POST /events, GET /events, GET /events/{id} endpoints work end-to-end with bearer auth.
3.3 event_invites schema + POST /events/{id}/invites issues token, sends invite email via Resend.
3.4 POST /invites/{token}/accept resolves token, creates user if needed, joins as editor.
3.5 PATCH /events/{id} with version check returns 409 on stale version, 200 + new version on success.
3.6 DELETE /events/{id}/members/{uid} (owner only) removing a member revokes their bearer scope to that event.

Phase 4 — Per-event resources

Outcome: All MVP resources (guests, households, tasks, budget items) have CRUD endpoints with version-checked PATCH. Still no mobile client.

# Task Done when…
4.1 guests + households schemas + endpoints full CRUD with version and event_id membership check.
4.2 tasks schema + endpoints including POST /tasks/{tid}/complete toggle.
4.3 budget_items schema + endpoints full CRUD.
4.4 GET /events/{id}/timeline server-side bucketing into Overdue / This Week / This Month / Later.
4.5 GET /events/{id}/export.json full deterministic dump for the user-owns-their-data story.
4.6 change_log table + helper every write inserts a change_log row before commit (transactional).
4.7 GET /events/{id}/changes?since=… catch-up feed for clients reconnecting.

Phase 5 — Android client skeleton + auth

Outcome: Android app installs, accepts an email, opens the magic link, talks to the API, lists the user's events.

# Task Done when…
5.1 Scaffold Android project Kotlin + Compose + Material 3, builds + runs via hpz-emu run.
5.2 Retrofit + OkHttp + serialization deps EventaApi interface compiles; sample /health call works.
5.3 Magic-link UI + deep link handler request screen, "check your email" screen, eventa://verify?token=… handler.
5.4 Bearer token storage encrypted shared prefs; auto-attach to Retrofit via interceptor.
5.5 EventListScreen calls /me, displays events; tap → EventDashboardScreen.
5.6 Local Room cache EventDao mirrors GET /events so offline read is possible.

Phase 6 — Mobile MVP feature surfaces

Outcome: Same five MVP areas (Brief, Guests, Checklist, Timeline, Budget) now exist on Android, talking to the backend, persisting via the API. Two devices can EDIT — but they don't yet see each other's changes live (that's phase 7).

# Task Done when…
6.1 Event Brief read + edit calls GET/PATCH /events/{id}; surfaces 409 conflict path.
6.2 Guest list + add/edit guest full CRUD via API, Room cache mirrors.
6.3 Contacts import permission flow + picker → POST guests.
6.4 Checklist + add/edit task full CRUD via API.
6.5 Timeline screen renders /events/{id}/timeline.
6.6 Budget Buckets + add/edit full CRUD via API.
6.7 Receipt photo upload multipart upload to POST /budget-items/{bid}/receipts.
6.8 Local-cache write-through every successful API write updates Room.
6.9 Optimistic UI + 409 retry helper reusable hook: PATCH → on 409 refresh + retry once + then surface "someone changed this."

Phase 7 — Real-time (WebSockets)

Outcome: Two phones in the same event see each other's edits within 1-2 seconds.

# Task Done when…
7.1 FastAPI WebSocket endpoint /api/v1/ws?bearer=… accepts auth, holds a connection.
7.2 Per-event rooms + subscribe protocol {type:"subscribe", event_id} validates membership and joins the room.
7.3 Server-side change broadcast every write under event_memberships fanouts a small {type, entity_type, entity_id, version} to all room members except the actor.
7.4 Android WebSocket client OkHttp WS, lifecycle-bound, exponential-backoff reconnect.
7.5 since=… catch-up on reconnect client tracks last cursor; reconnect calls GET /changes?since to fill gaps.
7.6 UI auto-refresh on push repository receives a small message → invalidates affected query → screens recompose.
7.7 Conflict UX polish "Alex updated this 5s ago" inline hint when reconciling a stale local edit.

Phase 8 — Push notifications (FCM)

Outcome: Even when both phones are sleeping, the second one gets a push within seconds of the first one editing.

# Task Done when…
8.1 Firebase project + creds on backend firebase-admin initialized; sends a test push to a known token.
8.2 POST /me/fcm endpoint client registers/refreshes its FCM token.
8.3 Server-side fanout helper on writes, look up other-member tokens, send data-only FCM.
8.4 Android Firebase Messaging integration client receives FCM; onMessageReceived triggers WS reconnect/refresh or shows a system notif.
8.5 POST_NOTIFICATIONS permission flow requested lazily after first invitation accept.
8.6 Notification kind→copy table "Alex marked 'venue booked' done" / "Gerda invited you to Jane's wedding" / etc.
8.7 Acceptance test on two physical devices edit on device A → device B sees push within ~3s.

Phase 9 — Hardening, polish, release readiness

Outcome: the V1 MVP is testable end-to-end on real Android devices, doesn't accidentally violate any non-goal, and is safe to invite real beta users to.

# Task Done when…
9.1 End-to-end smoke flow on 2 devices invite, accept, brief, guest, RSVP, task, deadline, budget, receipt — verified across both.
9.2 Loading / empty / error polish every screen has empty state, recoverable error UX, no infinite spinners.
9.3 Delete confirmations for destructive actions events / guests / tasks / expenses / receipts / removing members.
9.4 Accessibility pass content descriptions, scalable text, adequate touch targets.
9.5 Date / time / currency formatting pass locale-aware rendering.
9.6 Backend rate limits + abuse protection login throttle, invite throttle, basic per-bearer rate limit.
9.7 DB backup automation on tiny1 nightly pg_dump to off-server storage.
9.8 Sentry-style error reporting (optional) server + Android both report unhandled exceptions to a self-hosted (glitchtip) or hosted (sentry) endpoint.
9.9 Release-build signing + sideload APK release variant signs, builds, installs on a real phone, MVP path verified end-to-end.
9.10 Final acceptance pass all 5 MVP areas + invites + WS sync + FCM push + JSON export — no non-goal violations.

Numbers

What this plan replaces

This plan supersedes the round-5 single-user plan. Specifically it adds:

It drops: