Review: Bug: GET /teams/{id} returns 422 -- tenant_id not auto-resolved like admin routes

review-877-2026-04-07 Doc

review ready

Verdict: READY

Re-review after refinement. Previous verdict: NEEDS_REFINEMENT (review-877-2026-04-07). All recommendations addressed.

Template Completeness

Issue body not directly readable via MCP toolset (list_issues returns titles only). Refinements confirmed via prompt context: expanded scope to all routes in teams.py, added file targets, referenced schedule.py _get_tenant helper, fixed story label.
  • [x] Type -- Bug (from title prefix)
  • [x] Repo -- forgejo_admin/basketball-api (correct)
  • [x] File Targets -- teams.py + test_teams.py (verified in codebase)
  • [x] Fix pattern -- schedule.py _get_tenant helper referenced
  • [?] Remaining template sections -- cannot verify body directly, accepted based on refinement confirmation

Traceability

  • [x] story:WS-S10 label -- "As an admin, I want to manage coaches and assign them to teams so that teams have leadership." Teams routes serve admin team management. Acceptable fit.
  • [x] story note verified -- WS-S10 found in project-westside-basketball user-stories section under Admin (Marcus) stories
  • [x] arch:basketball-api label present
  • [ ] arch note MISSING -- search for "arch-basketball-api" returned zero results. Pre-existing gap, not introduced by this ticket. Non-blocking for this review.
  • [x] Forgejo issue -- forgejo_admin/basketball-api#372, confirmed open

File Targets

  • [x] src/basketball_api/routes/teams.py -- verified: 8 routes take tenant_id: str as explicit query param (lines 273, 293, 341, 372, 386, 463, 493, 541). create_team takes it in request body (TeamCreate model) which is a different pattern.
  • [x] tests/test_teams.py -- verified: 878 lines, 41 test functions. Extensive tenant_id={tenant.id} in query strings throughout. All need mechanical update.
  • [x] src/basketball_api/routes/schedule.py -- verified: _get_tenant helper at line 128-132 uses DEFAULT_TENANT_SLUG from registration service. This is the fix pattern.

Repo Placement

OK. Issue filed on forgejo_admin/basketball-api, fix is in basketball-api. Single repo scope.

Dependencies

  • Issue #262 (Extract shared _resolve_player helper) -- related DRY refactoring in same domain but independent. Not blocking.
  • No blocking dependencies identified.

Acceptance Criteria

Expected AC (inferred from bug description + codebase analysis):
  • All 8 GET/PATCH/DELETE/POST routes in teams.py auto-resolve tenant via DEFAULT_TENANT_SLUG (like schedule.py)
  • tenant_id query parameter removed from all 8 route signatures
  • create_team body.tenant_id may remain (creation explicitly specifies tenant) or be made optional with default -- agent should decide
  • All 41 existing tests updated to remove tenant_id from query strings
  • No regression in downstream consumers (westside-app, admin routes)
All criteria are agent-verifiable via test execution.

Blast Radius

Contained. tenant_id: str as a query parameter is isolated to teams.py -- no other route files use this pattern. admin.py, schedule.py, register.py, and password_reset.py all auto-resolve via DEFAULT_TENANT_SLUG already.
Clarification on "9 routes": The scope says all 9 routes, but create_team (POST "") takes tenant_id in the request body via TeamCreate model, not as a query param. The actual query-param fix applies to 8 routes. create_team is a judgment call for the implementing agent -- it can remain body-based or be made optional with a default.

Decomposition

No decomposition needed. 2 files (teams.py + test_teams.py), mechanical transformation (same pattern x8 routes + x41 tests). Single _get_tenant helper addition. Estimated agent time: 3-5 minutes. Fits within the 5-minute rule.

Recommendation

No action needed. All previous NEEDS_REFINEMENT items addressed:
  • [LABEL] story:WS-S1 -> story:WS-S10 -- DONE
  • [BODY] Scope expanded to all routes in teams.py -- DONE
  • [BODY] File targets added (teams.py + test_teams.py) -- DONE
  • [BODY] Fix pattern referenced (schedule.py _get_tenant) -- DONE
Pre-existing scope gap (arch note arch-basketball-api) is tracked but non-blocking.