Skip to content
How much to read

Knowledge base

projects / easemysemester / features

Privacy notice and terms, tested against the schema

v2's privacy notice and terms replaced one-paragraph stubs and are tested against the real schema, so a claim about what data is held cannot drift from what the database actually stores.

feature · current · updated 2026-09-16 · EaseMySemester case study

See overview and Google OAuth decision. Shipped 2026-09-10, public and ungated, ahead of OAuth review.

The problem it fixes

The old privacy stub told students the app keeps their name. It never has: the Google callback reads sub, email and email_verified, and the users table has no name column. A public page carried a false statement about what the app holds, and nothing in a review had caught it because nothing linked the page's claims to the schema.

The fix is a test, not just a rewrite

A disclosure list renders on the privacy page, and a test reads the real migrations and fails the unit lane when a table that references users(id) or carries an email column is missing from that list. The same test proves every table the page claims is deleted with the account actually cascades from users, so the deletion promise is only as true as the schema. Adding a per-user table later breaks the suite until the new page copy accounts for it.

What the pages disclose

The three OAuth scopes requested, Google's Limited Use disclosure, all seven personal-data tables, the cookies in use and what each is for, the four processors (Google, OpenRouter, Vercel, Neon) including that model providers may train on text sent to them, a 30-day deletion window, and a contact address. No governing-law clause, by decision.

A second test asserts properties invisible from inside the app that would otherwise break silently: both pages render with no session, neither imports the auth guard, the connection pool, or next/headers, the landing page links to both, and the notice carries a contact address and the Limited Use text. A production build confirms /privacy and /terms are the only static routes in the app.

Status

Shipped 2026-09-10 (issue unspecified in the commit). No migration was required.

This page was written by a model from my own repositories and checked by a second one against the same sources. I did not write it, and no version of it was approved by me before it went up. How that works, and where it can still be wrong.