fix: dedupe Alice Uwamahoro player rows (#420) #428
No reviewers
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!428
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "420-dedupe-alice-uwamahoro"
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?
Summary
Delete the stale Alice Uwamahoro submission (player id 201, parent id 175) caused by a typo'd email (
aliceuwamahoro13a.@gmail.com), keeping the canonical rows (player 202, parent 176). Implements basketball-api#420.Changes
alembic/versions/039_dedupe_alice_uwamahoro.py(new) — data-only migration that deletes player 201 + parent 175 with safety guards.Migration Notes
039(down_revision038), not040as the ticket anticipated. Current head onmainat the time of branching is038(038_add_declined_contract_status), so039is the next slot. basketball-api#422 can chain onto039as040when it lands.RuntimeErrorwith counts if any rows exist inplayer_teams,orders, orregistrationsfor player_id 201. No silent orphaning.parent_id = 175.op.execute()viasa.text()with bound params, matching the data-only style of migrations like035_set_player_contract_overrides.py.Player/Parentmodels are untouched.Test Plan
Validated locally against a sqlite sandbox pre-seeded with rows matching the ticket's state:
upgrade().SELECT COUNT(*) FROM players WHERE name = 'Alice Uwamahoro'->1parent_id = 176, parent 176 intact.upgrade()a second time on the post-migration state. No errors, no-op.ordersrow referencing player 201.upgrade()raisedRuntimeError: Refusing to delete player 201: dependent rows exist (player_teams=0, orders=1, registrations=0). Manual cleanup required before this migration can run.as expected.ScriptDirectorywalk confirms single head039chaining cleanly back through038 -> 037 -> ... -> base.ruff format+ruff checkclean.pytest tests/— 891 passed. 4 pre-existing collection/import failures unrelated to this migration (test_contract_reminder.py+ 3test_reconciliation.pycases missinggroupme_sdk) were observed; they fail identically onmainwithout this change.Post-merge (not part of this PR): ArgoCD sync +
alembic upgrade headagainst prod will execute the delete.Review Checklist
player_teams/orders/registrationsdowngrade()documented as no-op (deletion irreversible)op.execute()+sa.text()with bound params, matching existing data-only migration stylePlayer/Parentmodels untouchedruff format+ruff checkclean039 -> 038 -> ...)Related Notes
board-westside-basketball(APPROVED viareview-925-2026-04-10)PR #428 Review
DOMAIN REVIEW
Stack: Alembic data-only migration against Postgres (SQLAlchemy Core via
op.get_bind()+sa.text()). Verified against the post-force-push diff.Migration header (correct after rebase)
alembic/versions/042_dedupe_alice_uwamahoro.pyrevision = "042",down_revision = "041"041.Dedupe logic
players.id = 201(stale Alice submission from typo'd email).parents.id = 175(orphaned after player delete).SELECT 1 ... WHERE id = :pidpre-checks. Safe to re-run.player_teams,orders,registrations— raisesRuntimeErrorwith per-table counts rather than silently orphaning. Correct behavior forcontract_status='none'rows which should have zero dependents.downgrade()is an explicit documented no-op (deletion is irreversible, no row data retained to reconstruct). Acceptable pattern for data-cleanup migrations.sa.text()— no injection surface, matches style of existing data migrations like035_set_player_contract_overrides.py.Alembic / Postgres specifics
sa.text()+ dict params portable across sqlite (used in test sandbox) and Postgres (prod). Verified.ScriptDirectorywalk.BLOCKERS
None.
NITS
039_dedupe_alice_uwamahoro.pywithdown_revision 038. The code is correct (042/041) but the body should be updated for historical clarity. Non-blocking; route to plan Epilogue if desired.printbefore each delete so the alembic run output shows what was removed (helps post-deploy verification). Optional, not required.SOP COMPLIANCE
420-dedupe-alice-uwamahoro)PROCESS OBSERVATIONS
down_revision = "041"(not a specific predecessor filename), it's robust to swap between #427 and #426 — but whichever PR ends up at 041 must merge first. Flag for merge coordinator, do not block.SELECT COUNT(*) FROM players WHERE name = 'Alice Uwamahoro'returns 1 before closing #420.VERDICT: APPROVED