Normalize email on parent creation (Gmail dot + case) #418
Labels
No labels
domain:backend
domain:devops
domain:frontend
status:approved
status:in-progress
status:needs-fix
status:qa
type:bug
type:devops
type:feature
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
forgejo_admin/basketball-api#418
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Type
Bug
Lineage
Standalone — discovered during 2026-04-10 Westside Ops Streamlit session while investigating Marcus's jersey/contract list. Surfaced three concrete duplicate-email clusters in the parents table, all caused by the same root bug.
Repo
forgejo_admin/basketball-apiWhat Broke
The
parentstable accumulates duplicate rows for the same logical person because the uniqueness check on email is a literal string comparison, with no normalization for case or Gmail's dot-insensitive local part.Three concrete dupe clusters confirmed in prod today:
aliceuwamahoro13a.@gmail.comandaliceuwamahoro13.a@gmail.com— ONE Alice Uwamahoro, TWO parent rows (ids 175 and 176) + TWO player rows (ids 201 and 202), registered 6 minutes apart on 2026-04-08. Blocked contract creation during tonight's Marcus sync.marcusdraney23@gmail.comhas THREE rows with different capitalizations:marcusdraney23@gmail.com,Marcusdraney23@gmail.com,marcusdraney23@gmail.com. That's Coach Marcus Draney's own email — single person, three rows.draneylucas@gmail.comhas two rows (Lucas's own, likely test data, same mechanism).Gmail treats the local part as case-insensitive AND ignores dots — but the app's uniqueness check uses a literal string compare, so any capitalization difference or dot placement creates a new parent row. Every time this happens, downstream player / contract / order rows get orphaned or duplicated.
Repro Steps
alice.uwamahoro@gmail.comaliceuwamahoro@gmail.com(same address, dots removed)SELECT COUNT(*) FROM parents WHERE lower(email) LIKE '%aliceuwamahoro%'Alternative repro using capitalization:
test@gmail.comTest@gmail.comExpected Behavior
Registration with any email that Gmail would deliver to the same inbox should collapse to a single
parentsrow. Specifically:@gmail.comand@googlemail.comaddresses, dots (.) in the local part should be stripped before the uniqueness check+suffixtags should also be stripped for Gmail (alice+tag@gmail.com→alice@gmail.com)The normalized form is what gets stored in the database going forward. Existing non-normalized rows should still resolve on lookup (lookup normalizes the input before querying).
Environment
parentstable, registration POST handler, parent lookup functionsAcceptance Criteria
alice.@gmail.comandalice@gmail.comcollapse to one rowTest@gmail.com=test@gmail.com)+suffixtags collapse for Gmail (alice+tag@gmail.com=alice@gmail.com)Related
westside-basketball— project this affectsmarcusdraney23@gmail.comparent dedupe (ticket B, not yet filed)draneylucas@gmail.comparent dedupe (low priority, test data)