Migration 040: jersey_public_orders table #429
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
ldraney/basketball-api#429
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
Feature
Lineage
Standalone — part of System B production rollout. Architecture in
arch-jersey-intake. Blocks POST (#430) and GET (#432) endpoints. Revised 2026-04-10 (twice): first to addsubmitter_keycloak_subfor Keycloak-gated identity, then to fix stale migration number (014 → 031) and stale model path (models/submodule → flatmodels.py).Repo
forgejo_admin/basketball-apiUser Story
As Marcus
I want a dedicated
jersey_public_orderstable tied to Keycloak user identitySo that submissions are stored with verified
keycloak_subvalues (not just self-declared email), reconciliation against existing parents/players is precise, and System B orders are clearly separated from System A and System CContext
System B submissions are Keycloak-authenticated (see
westside-landing#243). Every row has a verifiedsubclaim from the JWT.emailandplayer_nameremain user-editable because (a) they may differ from the JWT-holder (parent submitting for child), (b) the form should be editable rather than locked to JWT identity.Actual migration head verified 2026-04-10:
030_add_registration_type_to_registrations.py(revision030, down_revision029). Next available revision is 031. Do NOT use any lower number — migrations 014 through 030 already exist.Actual model location verified 2026-04-10: all SQLAlchemy models live in a single flat file
src/basketball_api/models.py(31 classes, includingParentandPlayer). There is nomodels/subdirectory. The newJerseyPublicOrderclass must be appended tomodels.py, not split into a submodule.File Targets
Files to create:
alembic/versions/031_add_jersey_public_orders.py— Alembic migration,revision = "031",down_revision = "030"Files to modify:
src/basketball_api/models.py— append a newJerseyPublicOrderSQLAlchemy class at the end of the file. Do NOT modify any existing class.Files the agent should NOT touch:
alembic/versions/(013 through 030) — those are System C and other features, hands offsrc/basketball_api/models.py— append-onlyroutes/checkout.py,routes/jersey.py— other systemsSchema (exact)
In
downgrade():DROP TABLE jersey_public_orders. Do NOT drop thepgcryptoextension — other tables may rely on it.Acceptance Criteria
alembic upgrade headapplies cleanly against a fresh DB from scratch (runs 001 through 031)alembic downgrade -1reverses 031 cleanly (head becomes 030)alembic upgrade headre-applies 031 cleanly after the downgradepgcryptoextension is created if not present (idempotent)kq,tier,statusvaluessubmitter_keycloak_subis NOT NULLON DELETE SET NULLJerseyPublicOrderclass is importable frombasketball_api.modelsmodels.pyis modified (verify withgit diff main -- src/basketball_api/models.py— only additions below the final pre-existing class)Test Expectations
upgrade head → downgrade -1 → upgrade headround-trip succeedskq='kingdoms'→ IntegrityErrorsubmitter_keycloak_sub→ IntegrityErrorpytest tests/ -k jersey_public_orderConstraints
alembic/versions/030_add_registration_type_to_registrations.py(op.create_table, op.create_index, etc.)emailis NOT UNIQUE (same email can submit multiple orders)submitter_keycloak_subis NOT UNIQUE either (same Keycloak user can submit multiple orders)keycloak_usertable — we store thesubclaim as a string and Keycloak is the source of truthdown_revisionon any existing migrationChecklist
basketball-apimainJerseyPublicOrderimportablegit diff main -- src/basketball_api/models.pyshows only additionsRelated
westside-basketball— projectstory:WS-S31— admin public jersey intake linkarch-jersey-intake— architecture docfeedback_funnel_requires_auth.md— why every row has a verified Keycloak identityalembic/versions/030_*.py— style guide, hands offScope Review: NEEDS_REFINEMENT
Review note:
review-947-2026-04-10Scope, schema, and traceability are solid, but two factual claims in the body are stale and will block dev if not fixed.
Blocking [BODY] fixes required:
030_add_registration_type_to_registrations.py), not 013. Migrations 014-030 all exist. New file must bealembic/versions/031_add_jersey_public_orders.pywithrevision = "031",down_revision = "030".src/basketball_api/models/directory. All SQLAlchemy models (includingParent,Player) live in a single flat filesrc/basketball_api/models.py.JerseyPublicOrdermust be appended to that file, not placed in a new submodule. Remove the "or wherever project convention puts models" hedge.op.execute("CREATE EXTENSION IF NOT EXISTS pgcrypto")toupgrade()beforeCREATE TABLEsogen_random_uuid()resolves on a fresh DB.src/basketball_api/models.py— append only."[SCOPE] discovered:
arch-jersey-intakenote does not exist in pal-e-docs. Thearch:jersey-intakelabel is set but there's no backing note. Not a blocker for T2 itself — queue as a separate scoping ticket so T3/T5 have a real arch doc.Verified good:
project-westside-basketballuser-stories (Admin/Marcus)parents.idandplayers.idare bothInteger— schema is correctON DELETE SET NULLcorrect for reconciliation semanticsjersey_public_orderin basketball-api (additive-only, no blast radius)Fix the body, re-submit for re-review.
Migration 014: jersey_public_orders tableto Migration 031: jersey_public_orders tableMigration 031: jersey_public_orders tableto Migration 040: jersey_public_orders table