Review: Basketball-API client with Keycloak auth (re-review)
Verdict: READY
Re-review Context
Previous review:
review-597-2026-03-28 (verdict: NEEDS_REFINEMENT, 4 recommendations). All 4 recommendations have been addressed in the updated issue body. This re-review verifies the fixes and performs a fresh deep-verification pass against the basketball-api codebase.Template Completeness
- [x] Type — Feature
- [x] Lineage — depends on #4 (scaffold), Keycloak client provisioned by #1
- [x] Repo — forgejo_admin/westside-ai-assistant
- [x] User Story — clear who/what/why for Marcus as admin
- [x] Context — Keycloak client credentials flow, caching, typed functions, tenant slug hardcoded
- [x] File Targets — specific files to create and modify, plus do-not-touch boundaries
- [x] Acceptance Criteria — 5 testable conditions
- [x] Endpoint Reference Table — NEW. Full HTTP method + path for all 14 operations
- [x] Test Expectations — 4 unit tests plus run command
- [x] Constraints — httpx, internal URL, Keycloak token URL, tenant slug, bulk note, client-only
- [x] Checklist — PR, tests, no unrelated changes
- [x] Related — project page and architecture doc linked
Traceability
- [x] story:read-ops — read operations for program data
- [x] story:write-ops — write operations for program data
- [x] arch:A3 — Basketball-API Client component in domain model
- [x] Forgejo issue — forgejo_admin/westside-ai-assistant#5, open
Previous Recommendations — Resolution
- [x]
[BODY]Tenant resolution strategy — RESOLVED. Context says "Tenant slug is hardcoded to 'westside-kings-queens'". Constraints repeats it. Endpoint table bakes the slug into paths. - [x]
[BODY]Endpoint reference table — RESOLVED. Full table added with 7 reads and 7 writes, all with HTTP method + path. - [x]
[BODY]Rename assign_tryout_number to plural — RESOLVED. Nowbulk_assign_tryout_numberswith explicit BULK note in Constraints. - [x]
[BODY]Clarify Keycloak client provisioning — RESOLVED. Lineage now says "Keycloak client provisioned by #1."
File Targets
- [x]
app/basketball.py— to be created. Depends on #4 scaffold creating theapp/directory. Documented in Lineage. - [x]
tests/test_basketball.py— to be created. Test file for the client module. - [x]
requirements.txt— to be modified. httpx should already be present from #4. - [x] Do-not-touch boundaries clearly stated (basketball-api repo, app/groupme.py).
Endpoint Table Deep Verification
Verified all 14 endpoints against the basketball-api codebase (
~/basketball-api/src/basketball_api/):Reads (all correct):
- [x]
GET /admin/dashboard— admin router prefix/admin+ route/dashboard. No query params needed (auto-resolves tenant). - [x]
GET /admin/players— admin router prefix/admin+ route/players. No query params needed. - [x]
GET /players/{player_id}— players router mounted at both/api/playersand/players(SPA alias). Path works via SPA alias. Requires any auth. - [x]
GET /admin/teams— admin router prefix/admin+ route/teams. Auto-resolves tenant. Returns teams+players for draft board. - [x]
GET /tenants/westside-kings-queens/roster— roster router prefix/tenants+ route/{tenant_slug}/roster. Correct. - [x]
GET /api/subscriptions/overview— subscriptions router prefix/api/subscriptions+ route/overview. Correct. - [x]
GET /api/subscriptions— subscriptions router prefix/api/subscriptions+ route"". Correct.
Writes (all paths correct, minor query-param nit):
- [x]
PUT /players/{player_id}— SPA alias mount works. Requires any auth. - [x]
POST /teams/{team_id}/players— SPA alias mount works. Requires admin +?tenant_id=query param. Body is{"player_ids": [...]}(list, not single). See Nit 1. - [x]
DELETE /teams/{team_id}/players/{player_id}— SPA alias mount works. Requires admin +?tenant_id=query param. See Nit 1. - [x]
PATCH /admin/players/{player_id}/visibility— admin router. Correct. - [x]
POST /api/teams— teams router. Body includestenant_idfield. Correct. - [x]
POST /api/roster/westside-kings-queens/check-in/{player_id}— tryouts router (no prefix). Correct. - [x]
POST /tryouts/admin/westside-kings-queens/assign-numbers— tryouts router (no prefix). Correct. BULK operation documented.
Nits (non-blocking)
These are minor details an agent can resolve at implementation time. They do not block the ticket.
- Query param on team player operations:
POST /teams/{team_id}/playersandDELETE /teams/{team_id}/players/{player_id}both require a?tenant_id=query parameter in the basketball-api code. Since the client hardcodes the tenant slug, the implementing agent needs to resolve the tenant's numeric ID or pass the slug. The existing endpoint acceptsstrfor tenant_id, so passing the slug string should work. Not blocking because the agent will discover this from the basketball-api OpenAPI spec or error responses. - Path prefix inconsistency: Some write paths use
/teams/and/players/(SPA alias mounts) while others use/api/teamsand/api/players. Both work — the SPA aliases are registered at lines 82-83 of main.py. Cosmetic only. - assign_player_to_team body schema: The actual endpoint accepts
{"player_ids": [...]}(a list), not a single player_id. The function nameassign_player_to_team(singular) is fine as a client wrapper that takes a single ID and wraps it in a list. The agent can handle this.
Repo Placement
OK. Issue filed on forgejo_admin/westside-ai-assistant, work targets that repo. basketball-api is consumed, not modified. Single-repo scope.
Dependencies
- [x] #4 (FastAPI scaffold + GroupMe webhook handler) — board item #596, column: backlog. This ticket cannot execute until #4 merges. Documented in Lineage.
- [x] #1 (Provision infrastructure) — provisions the Keycloak client
westside-ai-bot. Now documented in Lineage. - [x] #9 (basketball-api default-deny NetworkPolicy) — discovered scope item on the board. The AI assistant pod needs network access to basketball-api. This is an infra dependency that must be resolved before runtime validation, but not before code implementation.
Acceptance Criteria
5 AC items are testable and specific. All can be verified by an agent via unit tests with mocked HTTP responses. The test expectations align with the AC. Run command is clear:
pytest tests/test_basketball.py -v.Blast Radius
Low. New client module in a new repo. No existing code modified. basketball-api consumed read-only. Rollback is trivial (revert one file). NetworkPolicy item (#9) already tracked separately for runtime access.
Decomposition
2 files to create, 1 file to modify, 5 AC, 4 test expectations, single repo. All work is in one module (
app/basketball.py) plus one test file and a requirements line. Fits within the three-thing limit (1 client class, 1 test file, 1 requirements update). Estimated agent time: 4-5 minutes. No decomposition needed.Recommendation
No action needed. All previous NEEDS_REFINEMENT items resolved. Three nits identified (query params, path prefix inconsistency, body schema) are non-blocking implementation details the agent will resolve at build time.