feat(platform): role-scoped join codes, head coach member management (T-043) - #14
Merged
Conversation
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…T-030) Whiteboard page (Brief step 16, PNG 01-05/14/34): toolbar, view menu (zones + thresholds), record/replay, and save into My Patterns, all persisted through the scoped query layer (backend/app/routers/whiteboard.py) into the boards and saved_patterns rows (doc 03 4.2/4.3). Delete is coach-only, enforced server-side (403 for players), and every saved pattern is author-stamped from the caller's own membership, never a client-supplied field. Fixes a real reload bug: the view menu's settings popover floated above the toolbar and, left open, could visually sit on top of a token (e.g. the ball's default spot, or any token near the toolbar on a phone-width board). A drag or click landing there hit the popover instead of the token, and for a checkbox's label that silently flipped a zone toggle the user never touched, which then persisted to the boards row on reload. Fixed by closing the view menu when recording starts (a deliberate mode switch) and, for phone-width boards where the popover has no gap in the pitch it can occupy without covering some token, pinning it to the viewport's top edge instead of floating over the pitch. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…, dev.sh overlap with T-031)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Systematic permission test suite covering every Brief section 3 table row in both roles, API and UI: backend/tests/test_permissions.py names each test after its table row (whiteboard, delete-a-pattern, library/ formations/identity, roster/fit-warnings, plus the additive-only and player-token-gets-403 principles), and e2e/permissions.spec.ts sweeps a player-role visit across all five pages asserting coach-only controls and data are absent from the DOM, not merely hidden. Audit result: no enforcement gaps found. Whiteboard delete, roster CRUD, and fit_warnings exclusion were already API-enforced from their own tickets (T-030, T-033); this suite pins that behavior explicitly against the table rather than leaving it implicit across five separate files. Two table rows (Suggest own playstyle, Sessions) have no API surface yet in this codebase state, only their models: marked as named, skipped tests with a reason, for T-041/T-042 to turn into real assertions rather than silently omitted. One known ambiguity is pinned, not changed, per this ticket's own instruction: the API returns join_code to any team member while the UI shows it to coaches only (docs/agent/STATE.md open founder question 2). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Player submits a free-text playstyle suggestion on their own roster profile, sees it pending; coach reviews from a team-wide queue with a gold badge and an Approve/Dismiss card; approve merges the text into players.playstyle_note (visible both roles), dismiss clears it with no merge. New backend/app/routers/suggestions.py enforces every direction at the API: only a player may submit, and only against their own linked roster row; only a coach may list the pending queue, approve, or dismiss (403 otherwise). No new migration: playstyle_suggestions already exists in the 0002 schema migration. Player-row linkage (needed for "own profile") did not exist yet before this ticket; app/routers/roster.py adds a minimal, UI-free claim: on a player's own GET /api/roster, an unclaimed row whose name uniquely matches their display_name becomes theirs.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
T-043: Founder decisions 1 + 2 (2026-07-16)
Role-scoped join codes: teams now carry a player code (the existing join_code, repurposed in place, no data loss) and a new unique coach_join_code (migration 0004, backfilled per team, fresh-DB chain proof 0001-0004). Joining with a code assigns that code's role; the account's own role never decides. Join codes are now coach-only in every API payload (keys absent for players, not null, following the fit_warnings precedent) — this replaces T-040's pinned-ambiguity test with the new contract.
Head-coach member management: the team creator (and only them, enforced via a dedicated require_head_coach dependency) can remove members and change member roles; other coaches see the member list but get 403 on mutations (proven in e2e even bypassing the UI). Minimal founder-directed UI in the team area: labeled code blocks for coaches, a collapsed Manage team panel, controls rendered only for the head coach.
Coverage
Notes for founder
🤖 Generated with Claude Code