stress-tested against 5,000 in a week; the real plan is ~1,000 in two, with weekly releases.
Readiness audit: today's ~90 users → your actual growth plan
Four independent audits (infrastructure/DB capacity, third-party service tiers, known bugs re-assessed for scale, and crisis-safety under load) plus a direct correction pass on facts this project had gotten wrong — run against a hypothetical 5,000-in-a-week worst case as a ceiling check. Now reframed against the real number: ~1,000 users in two weeks, with a weekly iOS release cadence available. Three items were fixed outright overnight with no decision needed from you (below); the rest are yours to triage. Choose Fix before scaling · Monitor · Accept risk per finding, then copy your decisions back.
The single biggest thing this audit found isn't a bug. It's a pattern.
Four separate things on this list — the severity-gate fix, the classify-affect data-corruption fix, the granular-consent/analytics-gating rebuild, and the revenuecat-webhook CI fix — were reported as "fixed" earlier this week based on reading the repository, without confirming they were actually deployed or shipped. Re-checking against the live function versions and the live App Store build tonight found: the classify-affect fix was never actually deployed (live was still the vulnerable version — this one is now genuinely fixed and verified live, see below); the consent/analytics-gating rebuild is real code sitting on a branch with no upstream, so every sign-up today still gets zero consent gating; same for the severity gate and the CI fix. None of these are bugs in the fixes themselves — the code is right. The gap was verification: "committed" was treated as "live." That's now fixed as a habit, not just as four individual items — every finding below states plainly whether it's live today, fixed but not shipped, or still broken either way.
Anthropic is a real data processor. It wasn't in the DPIA, the ROPA, or the privacy policy draft until tonight.
Earlier this week this project concluded recommend-plan was "100% Postgres FTS, no LLM call in the live path" — stated as verified fact, used to write the DPIA and ROPA, and repeated in the privacy-policy draft. It was wrong. The live app (v2.2, build 5, and every version since v2.1(4)) sends a version-gate header that activates a live Claude/Anthropic composition path — FTS is the fallback only, not the primary path. Separately, check-in text has always gone to Anthropic (via classify-affect/classify-crisis, unconditionally) — that part was documented correctly elsewhere in the project the whole time, just stated backwards in the ROPA. Net effect: both check-in free text and beliefs-plan intake are sent to Anthropic on essentially every user action. The DPIA, ROPA, deep-dive wiki, and the privacy-policy draft artifact have all been corrected tonight — Anthropic is now named as a processor everywhere it should be, and the privacy-policy draft's "never shared with anyone" line (which also missed this) has been rewritten. What's still missing: a signed DPA with Anthropic, and a consent step that names it specifically — the single highest-priority GDPR gap this whole project has found.
Everything below rode the same release: merged, pushed, built, and uploaded
Since this was first written, the consent gating, severity-gate fix, back-button race fixes, and the pseudonymization/encryption work all merged to iOS main (commit aa3bd52) and the app was archived as v2.3 (build 1) and uploaded to App Store Connect. Backend consolidated 4 divergent local branches and pushed to main (commit 094630f). Not yet true: none of this reaches a real user until Isabel/Marie attach the build to a version and submit it for review in App Store Connect — that step hasn't happened.
- classify-affect's data-corruption bug — deployed and verified live. The fix (validating the model's output against the real schema instead of silently corrupting on a bad response) was written days ago but never actually shipped — confirmed by downloading the function that's genuinely running in production and finding the old, unguarded version. Deployed for real, then re-downloaded and re-checked to confirm the fix is in the file that's actually live. Every check-in from this point on is protected.
- Missing database index on `emotional_sessions` — created and verified live. The busiest table in the schema had no index on `user_id`, meaning every check-in history read did a full-table scan. Confirmed the exact live column names directly against the database first (no guessing), created the index, then re-ran the query plan and confirmed Postgres now uses it. Purely additive — nothing about the app changed, it just got faster under load.
- The `stuck`/`stuckness` seed-file landmine — defused and pushed. Retagged, committed, and now live on backend `main`. Fully closed, not just staged.
- Two crisis-safety back-button races found and fixed (`EmotionalCheckIn.swift`'s `CheckinHeader`, and one level up in `AppView.swift` — the second reachable only via assistive-technology activation bypassing a visual-only overlay). Both merged into `main` and built into v2.3.
- A separate, more severe bug found afterward by a dedicated audit pass: a Swift decode crash that would have broken every check-in save/read and plan generation on live `main`. Pseudonymization Stage 3 dropped `user_id` from the API responses that `SupabaseEmotionalSession`/`BeliefsPlan` decode — both had it as a non-optional Swift property with no default, so every decode would have thrown. Found before merge (not by a real user hitting it), fixed by making both fields `UUID?`, verified against a real captured API response before shipping. Full pseudonymization (Stages 1–3, dropping `user_id` from `emotional_sessions`/`beliefs_plans`/`beliefs_plan_steps` entirely, replaced by a separate never-exposed `pseudonym` mapping table) plus AES-256-GCM encryption at rest on the five free-text columns are both live now — see the infra wiki's data-model section.
- Push tokens fixed at the root cause. Segment's Customer.io destination only ever reads a device token off a one-shot "Application Installed" event that fires before the async APNs round-trip usually completes — structurally, most fresh installs could never have delivered a token to Customer.io. Fixed by adopting Customer.io's own SDK for push-token registration only (Segment still owns all other tracking) — confirmed working end-to-end via the Customer.io dashboard.
Still NOT fixed, re-confirmed 2026-08-26: the per-function CI deploy filter below (see "revenuecat-webhook still isn't in the CI auto-deploy") — the real fix exists on an unmerged branch and was mistakenly believed fixed in an earlier pass of this project. See the infra wiki's Architecture section for the full detail.
Where GDPR actually stands, in plain terms
Update 2026-08-26 — moved from "written, not shipped" to "shipped, not submitted." The paragraph below described the gap as of the original audit; it's since been merged, built, and uploaded.
Done and live in production, today: account deletion fully erases the database (verified, cascading); crisis detection (3 independent layers) is real and tested; UK Shout 85258 crisis text line is live; classify-affect's data corruption bug fixed; account-deletion erasure propagation to Amplitude + Customer.io deployed and live-tested; full pseudonymization (Stages 1–3) + encryption at rest on the special-category tables, live.
Merged, built into v2.3 (build 1), uploaded to App Store Connect — NOT live until submitted: granular consent (3 separate toggles, replacing one bundled button), analytics/Meta no longer firing before consent, the severity-gate fix (both `EmotionalCheckIn.swift` and `CheckinRouter.swift` gates, confirmed at `>= 8` in the build), UK Shout in the in-flow crisis screen, the two crisis-safety back-button race fixes. All real, all correct, all sitting in an uploaded binary — the only remaining step is Isabel/Marie attaching it to a version and submitting for review in the App Store Connect UI. This is genuinely the next thing to do — not more engineering, an App Store Connect click.
Drafted, needs your + Isabel's sign-off, not published anywhere yet: the DPIA, the ROPA, the redacted-admin-views SQL, and the corrected Privacy Policy & Terms — all real documents now (not just wiki entries describing future plans), all in ~/Documents/allin-gdpr/ or the linked drafts.
Not started, needs a decision before it can be built: the age-gate threshold (16 vs 18), the Anthropic DPA, the Amplitude Destination Filter (needs you or Isabel clicking through Segment's dashboard — 5 minutes, zero code).
Genuinely blocked on someone outside this project: Segment's own support, for the erasure API access.
Short version: the engineering AND the release build are done. It's one App Store Connect submission away from live. That's the real state of GDPR readiness right now.
Not 5,000 in a week — ~1,000 in two weeks, with weekly releases available
The audit below was scoped against a hypothetical "5,000 next week" to stress-test worst-case capacity — that math still holds as a ceiling check (nothing found gets less true at a slower ramp). But the real plan you've described is gentler and, importantly, gives you a lever the original framing didn't account for: a weekly iOS release cadence. That changes several items below from "can't possibly land in time" to "this is exactly what the first or second release is for." Findings that need an actual App Store build (consent gating, the severity gate) are re-tagged below as release-cadence items, not scale emergencies — the urgency is starting the release process now, not a panic.
Does it survive 5,000 users?
Infrastructure: yes, gradually degrading, not falling over. Postgres is architecturally sound (everything goes through PostgREST, no raw per-request connections); the one real gap (a missing index on emotional_sessions.user_id) has since been fixed and verified live — no longer a scale risk. Third-party services: mostly fine, one real unknown. Amplitude, Customer.io, and RevenueCat all have comfortable-to-manageable headroom or just a bill increase if crossed — Segment's actual current plan tier could not be confirmed, and if it's still Free, a second overage lockout in a 6-month window severs the entire analytics + lifecycle-email pipeline at once. Crisis safety: materially stronger than when this was first written, one unknown remains. The three-layer design is real and mostly sound; the two back-button races that could discard a crisis classification result before it was checked are both fixed and built into v2.3. Layer B (the edge classifier) is still a single attempt with no retry that fails to "false" on any error, and all four AI functions still share one Anthropic account's rate limits — that part of the picture is unchanged, and it's still the one this audit is least confident about at 5,000 concurrent-ish users, specifically because the account's actual Anthropic usage tier couldn't be confirmed from this environment.