feat(store): add users.email_verified_at column + model plumbing (TASK-1935)#805
Merged
Merged
Conversation
…K-1935) Wave 1 of PLAN-1933 (email verification). Pure infra — nothing reads the column until Wave 3, so this is behaviourally a no-op and mergeable early. - Migration 070 (SQLite) / 048 (Postgres): add nullable email_verified_at TEXT, mirroring disabled_at. UNCONDITIONALLY backfill every existing row to verified (RFC3339 'Z'-suffixed) so no existing / OAuth / self-host account is write-locked on deploy (inverted vs password_set's conditional backfill). SQLite ALTER without IF NOT EXISTS; Postgres with it. - SAFE default = verified (DR-3): CreateUser / CreateOAuthUser write a verified timestamp unless UserCreate.Unverified is explicitly requested (only the future cloud self-serve branch will set that). A missed call site fails SAFE (verified), not write-locked. - models.User.EmailVerifiedAt + IsEmailVerified() (mirror IsDisabled). - Update userColumns + BOTH scan sites (scanUser AND the inline SearchUsers scan) so the admin user list keeps working. - Expose derived email_verified bool in sessionUserPayload for a later wave. Gates: make check + make test-pg both green (dual-dialect verified). Claude-Session: https://claude.ai/code/session_01HxBkAMiFBtCRJ2tKSCt3ST
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Wave 1 of PLAN-1933 (Pad Cloud email verification). Pure infra, no behavior change — nothing reads the column until Wave 3, so this is mergeable early behind the (not-yet-mounted) middleware.
What
070_email_verified.sql(SQLite) +048_email_verified.sql(Postgres): add nullableusers.email_verified_at TEXT, mirroringdisabled_at. SQLiteALTER TABLE ADD COLUMNwithoutIF NOT EXISTS(SQLite rejects it here); Postgres withIF NOT EXISTS.Z-suffixed viastrftime/to_char) so no existing / OAuth / self-host account is write-locked on deploy. This is inverted vspassword_set's conditional (OAuth-aware) backfill — there is no "was this user ever verified?" signal, and the correct answer for every pre-existing account is "verified".CreateUser/CreateOAuthUserwrite a verified timestamp unless a new explicitUserCreate.Unverifiedfield requests otherwise. Only the future cloud self-serve branch (Wave 3) will set it, so a missed call site fails SAFE (verified), not write-locked.models.User.EmailVerifiedAt+IsEmailVerified()(mirrorsIsDisabled());UserCreate.Unverified(zero value → verified).scanUserAND the inline scan inSearchUsers(missing the second breaks the admin user list at runtime with a column/target mismatch).email_verifiedbool insessionUserPayloadfor a later web wave.Tests
CreateUserverified by default; explicitUnverified: truepersists as NULL; OAuth users verified.scanUser+SearchUsers) return the field for verified and unverified users.IsEmailVerified()unit test.Gates
make check— green.make test-pg— green (dual-dialect verified; SQLite passing does not prove Postgres).https://claude.ai/code/session_01HxBkAMiFBtCRJ2tKSCt3ST