Summary
Workout Lens is moving from a single-user logger toward a tool for instruktører at the same gym (Sporty business unit). The primary value is helping an instruktør keep their sessions varied over time — and crucially, when several instruktører share a recurring class slot (e.g. Styrke Mandag 18:00 taught by different people on different weeks), they need visibility into the joint session history for that slot to avoid repetition. Before building this, we need an explicit decision on identity, sharing default, and the foundation table that links users to a gym.
This is a large epic — only the foundational decisions and the smallest schema change land here; sharing UI, joint-class views, opt-out toggles, and the temporal roles table are tracked as follow-up issues.
Priority
High (blocks: instruktør variety view, joint-class history, future profile/sharing/permission UI)
Decisions to record
- Primary user: instruktører at the same Sporty business unit. Class participants (the ~20 people in the room) are not modelled as users in this iteration.
- Sharing default: opt-out, scoped to same gym (same Sporty business unit). By default, an instruktør's sessions are visible to other instruktører at the same gym; they can opt out per session.
- Exercise model: keep per-user
exercise_library and denormalised snapshots in session_template_exercises (no canonical global exercise catalogue in this iteration).
- Joint-class aggregation: rely on existing
gym_calendar linkage — sessions already reference gym_calendar_id, so "all sessions for Styrke Mandag" is a query, not a schema change.
Smallest schema change
Add a user_gyms table linking users to a Sporty business unit. The role column is included now as a temporary placeholder; it will be replaced by a FK to a dedicated roles table (id, name, title, valid_from, valid_to) for temporal role tracking in a later issue.
create table user_gyms (
id uuid primary key default gen_random_uuid(),
user_id uuid not null references auth.users(id) on delete cascade,
sporty_business_unit_id int not null,
-- role is a temporary text placeholder; will be replaced by a FK to a
-- dedicated roles table (id, name, title, valid_from, valid_to) in a later issue.
role text not null default 'instruktor',
created_at timestamptz not null default now(),
unique (user_id, sporty_business_unit_id)
);
-- RLS
alter table user_gyms enable row level security;
create policy "users see own membership" on user_gyms
for select using (auth.uid() = user_id);
create policy "users manage own membership" on user_gyms
for all using (auth.uid() = user_id) with check (auth.uid() = user_id);
This unlocks the "same gym" visibility scope without any read-side changes yet — a follow-up issue will use it to expose joint-class history.
Acceptance criteria
Out of scope
- Any sharing UI (read or write)
- Coach ↔ client model
- Participant-side users (members attending the class)
- Global exercise catalogue / cross-user exercise canonicalisation
- Profile visibility / display name fields
- Cross-gym visibility
- Building the roles/titles lookup table with temporal validity (separate issue)
Summary
Workout Lens is moving from a single-user logger toward a tool for instruktører at the same gym (Sporty business unit). The primary value is helping an instruktør keep their sessions varied over time — and crucially, when several instruktører share a recurring class slot (e.g. Styrke Mandag 18:00 taught by different people on different weeks), they need visibility into the joint session history for that slot to avoid repetition. Before building this, we need an explicit decision on identity, sharing default, and the foundation table that links users to a gym.
This is a large epic — only the foundational decisions and the smallest schema change land here; sharing UI, joint-class views, opt-out toggles, and the temporal roles table are tracked as follow-up issues.
Priority
High (blocks: instruktør variety view, joint-class history, future profile/sharing/permission UI)
Decisions to record
exercise_libraryand denormalised snapshots insession_template_exercises(no canonical global exercise catalogue in this iteration).gym_calendarlinkage — sessions already referencegym_calendar_id, so "all sessions for Styrke Mandag" is a query, not a schema change.Smallest schema change
Add a
user_gymstable linking users to a Sporty business unit. Therolecolumn is included now as a temporary placeholder; it will be replaced by a FK to a dedicated roles table (id, name, title, valid_from, valid_to) for temporal role tracking in a later issue.This unlocks the "same gym" visibility scope without any read-side changes yet — a follow-up issue will use it to expose joint-class history.
Acceptance criteria
user_gymstable created via Supabase migration with RLS as aboverolecolumn added as a text placeholder defaulting to'instruktor'user_gymspurposebusiness unit 8reference inapp/api/sportySync.jsflagged as the membership default for first users (or a follow-up issue opened to remove the hardcode once membership lookup replaces it)sessions)id, name, title, valid_from, valid_to) and migration ofuser_gyms.roleto a FKOut of scope