Review: Public coaches endpoint — GET /public/coaches
Verdict: READY
Re-review of board item #430. Previous review (NEEDS_REFINEMENT) found three issues. All three have been addressed.
Template Completeness
- [x] Type — Feature
- [x] Lineage
- [x] Repo
- [x] User Story
- [x] Context
- [x] Security — Field Allowlist (bonus section, not required by template)
- [x] File Targets
- [x] Acceptance Criteria
- [x] Test Expectations
- [x] Constraints
- [x] Checklist
- [x] Related
Traceability
- [x] story:WS-S26 label — Westside Season 26 public site
- [x] arch:basketball-api label — basketball-api component
- [x] Forgejo issue — forgejo_admin/basketball-api#177, open
File Targets
- [x]
src/basketball_api/routes/public.py— Does not exist yet, but issue #176 (board item #429, currentlyin_progress) creates it. Dependency is now tracked viadepends:bb-176label. Verified: nopublic.pyorpublic_routerreferences exist in the codebase yet.
Model Field Resolution
Previous review flagged
bio, photo_url, and slug as missing from the Coach model. Resolution per comment #8098:- [x]
bio— Alembic migration scoped in this ticket (ALTER TABLE coaches ADD COLUMN bio TEXT) - [x]
photo_url— Alembic migration scoped in this ticket (ALTER TABLE coaches ADD COLUMN photo_url VARCHAR(500)) - [x]
is_public— Alembic migration scoped in this ticket (ALTER TABLE coaches ADD COLUMN is_public BOOLEAN NOT NULL DEFAULT true). Comment #8073 established this scope addition. - [x]
slug— Documented as computed at runtime (name.lower().replace(" ", "-")), not stored. Acceptable.
Verified: no Alembic migration for these coach columns exists yet. The Player model has
photo_url (line 182 of models.py) as prior art for the pattern.Repo Placement
OK — issue filed on
forgejo_admin/basketball-api, work belongs there. Single-repo change.Dependencies
- Blocking: Issue #176 (Public teams endpoint, board item #429) — now
in_progress(wastodoat previous review). Createspublic.pyand registers public router. Board item #430 hasdepends:bb-176label. FIXED from previous review. - Downstream: Board item #431 (westside-app#98 "SvelteKit public site — layout shell + 8 pages") has
depends:bb-176+bb-177, correctly documenting it waits on both public API endpoints.
Acceptance Criteria
All criteria are now testable:
- [x] "GET /public/coaches returns coaches list, no auth required" — testable
- [x] "Response schema is PublicCoachesResponse — allowlisted fields only" — testable
- [x] "Each coach includes a URL-safe slug derived from name" — testable
- [x] "photo_url points to MinIO path" — testable once migration lands and seed data populates the column
- [x] "No sensitive data in response" — testable via negative assertion
- [x] "Endpoint accessible without Bearer token" — testable
Note: The
is_public filtering (only return coaches where is_public = true) is scoped in comment #8073 but not listed as an explicit acceptance criterion in the issue body. An executing agent will see the comment, but adding a criterion would make it unambiguous. Minor nit, not blocking.Blast Radius
- No existing public router pattern in basketball-api — this is new territory introduced by #176.
- The
rolefield on CoachRole is an enum (head_coach,assistant,director). The public response will serialize the enum value as-is. The executing agent should use.valuefor human-readable strings. Minor implementation detail, not a scope issue. - No other services consume coach data publicly. Only downstream consumer is westside-app's public site (board item #431).
- The Alembic migration pattern has prior art:
002_add_player_profile_fields.pyaddedphoto_urlto the players table.
Previous Findings Disposition
- Missing dependency label — FIXED.
depends:bb-176now on board item #430. - Model field gaps — FIXED. Alembic migration for
bio,photo_url,is_publicexplicitly scoped in comments #8073 and #8098. - Untestable acceptance criterion — FIXED.
photo_urlcriterion is testable once migration lands and data is seeded.
Recommendation
No action needed. All three previous findings are resolved. Ticket is ready for execution.
Optional improvement (not blocking): Add "Only coaches with
is_public = true are returned" as an explicit acceptance criterion in the issue body, since this behavior is currently only documented in comment #8073.