Release 1.37.1: group admins, per-group permissions, shared players, new Groups page, mobile bar #89

Merged
pyaniz merged 2 commits from staging into main 2026-10-24 13:04:43 -05:00
Owner

Type: Feature (minor) - VERSION 1.36.11 -> 1.37.0. Design: .gsd/proposals/group-administration.md (not committed) plus the shared-players addendum; all three phases in one release as agreed.

What

Roles. group_members.role ('admin'|'member'); the owner stays groups.owner_id. Ladder: global admin > owner > admin > member (share-link guests read only). Admins must be registered; only the owner demotes/removes admins, transfers or deletes. Existing data: owners unchanged, everyone else a member, so nothing changes until someone is promoted.

Per-group permissions. 10 capabilities with a minimum role, defaults = the old behaviour, overridden per group in group_settings as perm.<capability>. One engine in groups/permissions.py (get_group_role, can, assert_can, assert_group_owner, pure capability_allowed). Migrated: record / edit / delete plays, sessions, native calendar events, group update and settings, creating players inside a group, clearing group votes (now admins too). can_administer_group = owner/admin/global admin, can_write_group_content = any role.

Players shared across groups. players.created_in_group_id = home group (backfilled to the first group). GET /groups/{id}/candidates lists players from the caller's other groups; POST /groups/{id}/members creates a player inside the group and/or adds existing ones: unclaimed players are added directly, players with an account get an invitation (group_invites, accept/decline on their Groups page). Identity edits of an unclaimed player need edit_players in the home group (fallback: any group containing them). PUT /groups/{id} now applies membership as a diff, so roles are no longer wiped.

Groups page. Rewritten as list + workspace (two screens on phones) with Players / Overview / Settings tabs: role chips, red-italic no-account players, add (new or from my other groups, duplicate hints), edit, make/remove admin, claim, remove / leave, invitations, name + calendar, links, sync, display options, the permissions matrix, transfer ownership and delete. The global Players table is gone.

Migration V58 (role, created_in_group_id + backfill, group_invites). Docs: docs/PERMISSIONS.md, docs/features/GroupManagement.md.

Review hot spots

  • groups/permissions.py and the 16 call sites that moved to assert_can (plays, sessions, calendar): please skim for anything that should stay stricter or looser.
  • Behaviour changes on purpose: group admins exist; an unclaimed player can now only be edited via its home group's admins (before: anyone); delete group is owner-only.
  • candidates/members only reveal players the caller already shares a group with.

Testing

  • 6 new unit tests for the capability rules (backend/tests/test_group_permissions.py), engine tests still pass, npm run build passes, Python compiles.
  • Not run against a database or in a browser. On staging please check: V58 applies; the Groups page as owner, admin, member and with a share link; add a player to two groups through From my other groups; invite a registered player and accept it; promote and demote; change a permission (for example make recording plays admin-only) and see a member refused; transfer ownership; a phone-width pass.

🤖 Generated with Claude Code

**Type:** Feature (minor) - VERSION 1.36.11 -> 1.37.0. Design: `.gsd/proposals/group-administration.md` (not committed) plus the shared-players addendum; all three phases in one release as agreed. ## What **Roles.** `group_members.role` ('admin'|'member'); the owner stays `groups.owner_id`. Ladder: global admin > owner > admin > member (share-link guests read only). Admins must be registered; only the owner demotes/removes admins, transfers or deletes. Existing data: owners unchanged, everyone else a member, so nothing changes until someone is promoted. **Per-group permissions.** 10 capabilities with a minimum role, defaults = the old behaviour, overridden per group in `group_settings` as `perm.<capability>`. One engine in `groups/permissions.py` (`get_group_role`, `can`, `assert_can`, `assert_group_owner`, pure `capability_allowed`). Migrated: record / edit / delete plays, sessions, native calendar events, group update and settings, creating players inside a group, clearing group votes (now admins too). `can_administer_group` = owner/admin/global admin, `can_write_group_content` = any role. **Players shared across groups.** `players.created_in_group_id` = home group (backfilled to the first group). `GET /groups/{id}/candidates` lists players from the caller's other groups; `POST /groups/{id}/members` creates a player inside the group and/or adds existing ones: unclaimed players are added directly, **players with an account get an invitation** (`group_invites`, accept/decline on their Groups page). Identity edits of an unclaimed player need `edit_players` in the home group (fallback: any group containing them). `PUT /groups/{id}` now applies membership as a diff, so roles are no longer wiped. **Groups page.** Rewritten as list + workspace (two screens on phones) with Players / Overview / Settings tabs: role chips, red-italic no-account players, add (new or from my other groups, duplicate hints), edit, make/remove admin, claim, remove / leave, invitations, name + calendar, links, sync, display options, the permissions matrix, transfer ownership and delete. The global Players table is gone. **Migration V58** (`role`, `created_in_group_id` + backfill, `group_invites`). Docs: `docs/PERMISSIONS.md`, `docs/features/GroupManagement.md`. ## Review hot spots - `groups/permissions.py` and the 16 call sites that moved to `assert_can` (plays, sessions, calendar): please skim for anything that should stay stricter or looser. - Behaviour changes on purpose: group admins exist; an unclaimed player can now only be edited via its home group's admins (before: anyone); delete group is owner-only. - `candidates`/`members` only reveal players the caller already shares a group with. ## Testing - 6 new unit tests for the capability rules (`backend/tests/test_group_permissions.py`), engine tests still pass, `npm run build` passes, Python compiles. - **Not run against a database or in a browser.** On staging please check: V58 applies; the Groups page as owner, admin, member and with a share link; add a player to two groups through *From my other groups*; invite a registered player and accept it; promote and demote; change a permission (for example make recording plays admin-only) and see a member refused; transfer ownership; a phone-width pass. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Feature: group roles, per-group permissions, shared players and a new Groups page (1.37.0)
All checks were successful
Build and Publish Containers / build-and-push (push) Successful in 51s
Mirror to GitHub / mirror (push) Successful in 21s
a1cf696e46
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
pyaniz changed title from Feature 1.37.0: group admins, per-group permissions, shared players, new Groups page to Release 1.37.1: group admins, per-group permissions, shared players, new Groups page, mobile bar 2026-10-24 12:15:18 -05:00
Fix: slim static mobile bottom bar, player header in the mobile sidebar, (Elo) on the ranking card (1.37.1)
All checks were successful
Build and Publish Containers / build-and-push (push) Successful in 40s
Mirror to GitHub / mirror (push) Successful in 22s
a83cfa6bf4
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
pyaniz merged commit 195601d57e into main 2026-10-24 13:04:43 -05:00
Sign in to join this conversation.
No description provided.