Review: Seed Gmail OAuth token into oauth_tokens DB table for resilience

review-657-2026-03-29 Review

review approved

Verdict: APPROVED

The scope is valid and the issue is well-written. However, the reviewer notes a critical context gap: the code-level feature already exists. The remaining work is an ops-level task (running the seed script against production). This does not block approval -- the issue correctly identifies the gap and the file targets are accurate enough to guide an agent to the right place.

Template Completeness

  • [x] Type -- Feature
  • [x] Lineage -- discovered scope from westside-landing #169
  • [x] Repo -- forgejo_admin/basketball-api
  • [x] User Story -- as a superadmin, survive pod restarts
  • [x] Context -- explains file-based token limitation and outage root cause
  • [x] File Targets -- present (see accuracy notes below)
  • [x] Test Expectations -- 3 test categories listed
  • [x] Constraints -- 3 constraints documented
  • [x] Acceptance Criteria -- 4 criteria listed
  • [x] Checklist -- present
  • [x] Related -- links to #169 and project page

Traceability

  • [x] story:WS-S21 label -- password reset / auth self-service story (7 sibling items on board, including done items #223, #239, #617, #644)
  • [x] arch:basketball-api label -- correct, all changes are in basketball-api
  • [x] Forgejo issue -- forgejo_admin/basketball-api#242, open

File Targets

  • [x] src/basketball_api/services/email.py -- verified: exists, already contains DB-backed token loading via get_gmail_client() (lines 29-57) with fallback to file-based tokens
  • [ ] src/basketball_api/models/ -- INACCURATE: no models/ directory exists. The OAuthToken model is in src/basketball_api/models.py (single file, line 479). Issue should say models.py not models/
  • [x] Migration script -- verified: alembic/versions/021_add_oauth_tokens_table.py already exists and creates the oauth_tokens table
  • [x] (Undocumented) src/basketball_api/services/token_store.py -- already implements full token load/save/refresh/seed lifecycle (176 lines)
  • [x] (Undocumented) scripts/seed_oauth_token.py -- CLI seed script already exists
  • [x] (Undocumented) tests/test_token_store.py -- 12 tests covering save/load, refresh, seed, and integration

Repo Placement

OK. Issue filed on basketball-api, all code changes are in basketball-api. No cross-repo impact -- k8s secret mounting stays unchanged per constraints.

Dependencies

  • [x] Board item #229 (Spike: Gmail OAuth token persistence in Postgres) -- done, closed as basketball-api#130. This spike already built the entire feature described in #242.
  • [x] Board item #644 (Bug: Password reset flow not working) -- done, the outage that discovered this gap.
  • [x] Phase 21 (board item #288, Enterprise Auth) -- in backlog, blocked by platform phase-28. Parent story arc, not a direct dependency.
  • No in_progress blockers found.

Acceptance Criteria

All 4 acceptance criteria are already met by existing code:
  • [x] "oauth_tokens table seeded on startup or via migration" -- migration 021 creates the table; seed_from_file() + CLI script exist. Remaining gap: run seed against prod.
  • [x] "Token refresh logic uses DB store as primary, file as fallback" -- get_gmail_client() lines 42-57 do exactly this.
  • [x] "Pod restart does not break email sending" -- DB-backed tokens persist across restarts by design.
  • [x] "Monitoring/logging when token refresh fails" -- token_store.py lines 152-153 and 169-173 log failures.
The remaining work is purely operational: run python scripts/seed_oauth_token.py westsidebasketball against prod (or equivalent kubectl exec). An agent dispatched against this issue would find nothing to code.

Blast Radius

Low. Gmail OAuth is used only by basketball-api's email service. No sibling services consume token_store.py. The pal-e-mail service uses its own gmail-sdk integration and is an archive candidate per memory.

Decomposition Assessment

No decomposition needed. The actual remaining work (seed prod DB) is a single ops command, well under 5 minutes. 1 discrete change, 0 file targets to modify, 0 repos to touch with code. If the issue is reframed as an ops task rather than a feature, it is a single-step action.

Recommendation

  • [BODY] Fix file path: src/basketball_api/models/ should be src/basketball_api/models.py (single file, not directory)
  • [BODY] Reframe scope: the code feature is already implemented (spike #130). The remaining work is ops -- seed the prod oauth_tokens table by running the existing seed script. Consider changing Type from Feature to Task.
  • [BODY] Add file targets for existing code: services/token_store.py, scripts/seed_oauth_token.py, tests/test_token_store.py -- these are the actual implementation files an agent needs to know about.