#601 ·
evolutivaFleet-wide Gmail dot/plus normalization: block duplicate accounts at the DATA LAYER (Mars+Pluto+Venus)
- Ref
#601(#601)- Project
evolutiva- Status
- blocked
- Priority
- high
- Type
- task
- Assigned
- evolutiva-pm-cc-w pm
- Created by
- —
- Blocked reason
- awaiting elazar
- Created
- 2026-06-01T17:09:24.725Z
- Updated
- 2026-06-03T21:33:15.829Z
Questions (1 open)
-
bin-whey-cc gates outward GitHub pushes on a DIRECT Elazar word (not a PM relay). The commons §Email Identity codification commit (shared/md/evolutiva-commons.md, the regression-guard for the fleet gmail-dedup — all 3 apps ALREADY enforce dedup at the DB, this just writes the rule so a future app can't reintroduce the bug) needs your explicit push-approval delivered to bin DIRECTLY: your CLI word, or a from=elazar DM to bin-whey-cc. Approve the commons --minor push?
Event log
-
wi cli
-
REQUIREMENT (Elazar, 2026-06-01, upset - recurring): make EVERY Evolutiva app PREVENT Gmail dot/plus duplicate accounts permanently. Real case (Mars): somofran@gmail.com AND somo.fran@gmail.com became 2 accounts - must be impossible. Audit ALL apps, not just Mars. He does NOT want 'fixed a record' reports - wants structural prevention. CANONICAL SOLUTION (research-backed): - Gmail (gmail.com + googlemail.com) IGNORES dots in local-part and everything after '+': a.b@gmail == ab@gmail == ab+x@gmail = ONE mailbox. (Other providers: dots ARE significant - do NOT strip.) - normalizeEmail(email): trim+lowercase; split local@domain; IF domain in {gmail.com, googlemail.com}: remove all '.' from local, drop '+tag' suffix, canonicalize domain->gmail.com; ELSE lowercase only. Co-locate with isAllowlistedEmail in src/lib/email-allowlist.ts per app. - Apply at EVERY boundary: signup/invite/first-contact, auth/callback allowlist, every lookup/compare, every write. - THE GUARANTEE = DATA LAYER: add normalizedEmail column + UNIQUE index per app. DB rejects the dup even if code is bypassed. Code-only normalization is what keeps failing; the unique constraint is the wall. - One-time per app (unavoidable cleanup, NOT the deliverable): backfill normalizedEmail, detect+merge existing dup pairs, THEN add unique constraint. ROLLOUT (post-compact, parallel by project): per app (mars/pluto/venus) - audit email entry/compare/store points; add shared normalizeEmail; *-db agent adds normalizedEmail col + unique index (lookup-table/constraint discipline per evolutiva-db.md - note: unique index is fine, this is NOT a native-enum); dedup migration; verify zero dotted dupes remain + invite/signup rejects a dotted variant of an existing account. Pre-push audit design-ping required (auth/email path per commons Push-to-Done). Route per-project via pm-mars/pm-pluto/pm-venus. PM owns rollup; generalpm owns Elazar channel.
-
SPEC CONVERGED (3 PMs, 2026-06-01 14:14). Final §Email Identity contract: (1) one canonicalizeEmail() - gmail.com/googlemail.com ONLY: strip local-part dots + drop +subaddress + fold googlemail->gmail + lowercase; ALL other domains lowercase-only (dots significant; Workspace custom domains do NOT ignore dots - scope frozen to gmail/googlemail pending venus researcher). (2) GUARANTEE = partial UNIQUE INDEX on canonicalizeEmail(email) WHERE deletedAt IS NULL, with the fn marked IMMUTABLE. Functional index = no stored canonical column required; soft-delete-aware partial. (3) NAMING: fleet fn = canonicalizeEmail(); do NOT repurpose normalizedEmail (it's lowercase-only in mars/pluto = the actual root bug); any denormalized canonical column = emailCanonical. (4) OAuth: Supabase returns RAW Google address -> MUST canonicalizeEmail() before auth-link lookup or login spawns a 2nd account (battle-tested in pluto prod). (5) Migration: dedup-scan + merge existing collisions FIRST, THEN add the index. (6) Unique-violation -> 'account exists, sign in/recover' UX. SCRIBE = pm-mars (posts one locked spec). Children: mars #315 (db-mars implementing now, Somoza dupe already reconciled). pluto+venus open their own. MY PENDING: when mars posts locked spec TEXT, dispatch bin-whey-cc to make the ONE commit to shared/md/evolutiva-commons.md (commons file outside venus checkout per #590; PMs don't edit commons). Then track 'enforced ✓' per app for generalpm's Elazar rollup.
-
Mars WI#315 ENFORCED 2026-06-03 18:30 ART (pm-mars-cc): canonicalizeEmail() fn + UNIQUE INDEX users_canon_email_idx ON users(canonicalizeEmail(email)) WHERE deletedAt IS NULL applied+verified. Pre-scan 442 live users / 442 distinct canonical / ZERO collisions. Fleet per-app DB enforcement now 3/3: venus ✓ pluto ✓ mars ✓. REMAINING: commons §Email Identity codification commit (bin-whey-cc) — regression-guard, not yet in shared/md/evolutiva-commons.md (last commit f624f0a 2026-05-31, no canonicalizeEmail present).
-
q#44 to=elazar: bin-whey-cc gates outward GitHub pushes on a DIRECT Elazar word (not a PM relay). The commons §Email Identity codification commit (shared/md/evolutiva-commons.md, the regression-guard for the fleet gm
-
awaiting elazar