Adds an invite-only mode for group classes. An invite-only class (us_offerings.access_mode = invite_only) is hidden from the public catalog — it never appears in the student booking/group-class list — and is reachable only when the instructor explicitly lets someone in.
Instructor paths (My Lessons → My Group Classes)
Add students directly — enrols selected registered students immediately with a pending payment at the class price.
Make available — grants selected registered students access (multi-select) so the class appears in their list; they self-enrol through the normal paid flow. Each is emailed a notice.
Invite by email — for an address with no account: a tokenised registration invite tied to the class is emailed; after they register the invite-only class becomes enrollable and they choose whether to enrol. An address that already has an account is treated as make available.
Grants and invites email the person, except when a pending invite already exists for that email — the grant is attached to the existing invite and no second link is sent.
Enforcement & visibility
GET /offerings returns public offerings plus the invite-only ones the caller has a grant for.
POST /enrollments rejects an ungranted student on an invite-only class with 403 invite_required; a successful enrolment flips the grant invited → enrolled.
Data model
us_offerings.access_mode (public / invite_only)
us_invites.offering_id (personal invite tied to a class)
new us_group_access table (per-person grants)
Schema ships under the unreleased 1.1.0 — no version bump.
## Summary
Adds an **invite-only** mode for group classes. An invite-only class (`us_offerings.access_mode = invite_only`) is hidden from the public catalog — it never appears in the student booking/group-class list — and is reachable only when the instructor explicitly lets someone in.
## Instructor paths (My Lessons → My Group Classes)
1. **Add students directly** — enrols selected registered students immediately with a **pending payment** at the class price.
2. **Make available** — grants selected registered students access (multi-select) so the class appears in their list; they self-enrol through the normal paid flow. Each is emailed a notice.
3. **Invite by email** — for an address with no account: a tokenised registration invite tied to the class is emailed; after they register the invite-only class becomes enrollable and they choose whether to enrol. An address that already has an account is treated as *make available*.
Grants and invites email the person, **except** when a pending invite already exists for that email — the grant is attached to the existing invite and no second link is sent.
## Enforcement & visibility
- `GET /offerings` returns public offerings **plus** the invite-only ones the caller has a grant for.
- `POST /enrollments` rejects an ungranted student on an invite-only class with `403 invite_required`; a successful enrolment flips the grant `invited → enrolled`.
## Data model
- `us_offerings.access_mode` (`public` / `invite_only`)
- `us_invites.offering_id` (personal invite tied to a class)
- new `us_group_access` table (per-person grants)
Schema ships under the unreleased 1.1.0 — no version bump.
## Tests
- `composer test` — 487 tests pass
- `composer lint` (PHPStan level 10) — clean
- `composer cs` (PHPCS) — clean
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Group classes can now be marked invite-only (us_offerings.access_mode).
Invite-only classes are hidden from the public catalog and reachable only
when the instructor lets someone in via one of three paths, managed from
My Lessons -> My Group Classes:
- Add students directly: enrols them now with a pending payment.
- Make available: grants registered students access to self-enrol through
the normal paid flow (multi-select, emailed a notice).
- Invite by email: tokenised registration invite tied to the class for a
non-account address; after they register the class becomes enrollable.
Reuses an existing pending invite instead of sending a second link.
New us_group_access table records grants; GET /offerings merges granted
invite-only classes for the caller; enrolment requires a grant
(403 invite_required) and flips it to enrolled on success.
composer test (487), composer lint, composer cs all pass.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Adds an invite-only mode for group classes. An invite-only class (
us_offerings.access_mode = invite_only) is hidden from the public catalog — it never appears in the student booking/group-class list — and is reachable only when the instructor explicitly lets someone in.Instructor paths (My Lessons → My Group Classes)
Grants and invites email the person, except when a pending invite already exists for that email — the grant is attached to the existing invite and no second link is sent.
Enforcement & visibility
GET /offeringsreturns public offerings plus the invite-only ones the caller has a grant for.POST /enrollmentsrejects an ungranted student on an invite-only class with403 invite_required; a successful enrolment flips the grantinvited → enrolled.Data model
us_offerings.access_mode(public/invite_only)us_invites.offering_id(personal invite tied to a class)us_group_accesstable (per-person grants)Schema ships under the unreleased 1.1.0 — no version bump.
Tests
composer test— 487 tests passcomposer lint(PHPStan level 10) — cleancomposer cs(PHPCS) — clean🤖 Generated with Claude Code