Skip to Content
Living documentation — last reviewed 2026-05-28
FeaturesManaged MembersManaged Members — Rollout Runbook

Managed Members — Rollout Runbook

How phase 1 reaches production, how a gym is switched on, and how to switch it off. The flag model is specified in behavior.md §6a; the migration in data-model.md §5.

1. Merge = migrate

Railway runs pnpm db:migrate as the API’s preDeployCommand (apps/api/railway.toml:15). Merging PR #526 to main applies 0168_managed_members to production before the new API serves traffic. There is no separate migration step, so the owner’s approval to merge is the approval to migrate (docs/runbooks/migrations.md, hard rule 2).

Before merging:

  1. CI green on the PR, the branch up to date with main, and 0168’s journal when greater than every earlier entry (migrations.md, monotonic-when).
  2. drizzle-kit generate reports no changes.
  3. Review the SQL once more for the two data-touching steps: empty-string emails → NULL (before the partial unique index), and the form-signature backfill.

What the migration does to existing data: sets users.email = '' to NULL, relabels every historical form signature as a self-signature, and adds defaulted columns. No existing row is re-keyed.

2. Deploy with the flag off

The flag managed-members is unset in PostHog, so it is off for every org. With it off, adults see no change apart from the small ungated items: who-booked attribution on bookings, a non-training adult promoted to trains when they get a plan, and nullable-email handling.

After deploy, check:

  • Sentry and Better Stack for new errors in users, memberships, bookings and notifications for an hour.
  • An existing member signs in, books and cancels; a staff member opens the member list and a member record (no Add child, no Responsible adults filter, no household section).

3. Before turning it on for a pilot gym

Not yet verified outside the review stack:

CheckWhy
Push on a real deviceNotifications about a child must reach the adult’s device and open as that child.
Teen sign-up block through the live Clerk webhookSign up with an address a child record holds: expect “ask your gym to link your account” and an audit row.
Youth-consent gateCreate a parental_consent_youth template; a child can’t book until their adult signs it.
Arbox / Fizikal mirror for the pilotThe Arbox direct sync skips children (only the CSV import creates them). Confirm neither mirror fails on, or overwrites, a member with no email.
A real card payment for a child’s planThe adult is charged and receives the receipt; the child’s record shows the plan.

4. Turning it on for a gym

  1. The gym’s plan must include managed_members (Pro and above). The flag alone does nothing on a lower tier.
  2. In PostHog, enable managed-members for that org: group type organization, key = the org id. The API evaluates with distinctId = orgId and groups: { organization: orgId }; only true counts.
  3. It applies on the next request. Staff now see Add child, the Responsible adults filter and the household section; parents see Family and, once a link is confirmed, the switcher.

5. Turning it off

Switch the flag off for the org in PostHog. It takes effect immediately, with no deploy.

StaysStops
Children as ordinary members: roster, bookings, plans, billing; staff manage them from the dashboard.Staff Add child, the filter, the household section.
Notifications and receipts about a child still go to their adults.Parents’ Family area, the switcher and acting for a child (403 managed_members_disabled).
Every link, attestation and audit row.The youth-consent booking requirement: a child books on what an adult does.
The safety guards and the teen sign-up block.Guardian signing; CSV rows becoming children.

Turning it back on restores everything as it was.

6. What not to do

  • Don’t roll the API back below this release once the migration has run. Earlier code expects users.email to be a string, and phone-only sign-ups now hold NULL. Use the flag to switch the feature off.
  • Don’t hand-write a down migration. There is none, and undoing it is clean only while no gym has children: a child has no email to restore.

7. Mobile

Mobile (taikan-mobile PR #128) needs the new shared types. After #526 merges: publish @taikan/shared, bump it in #128, let its CI go green, then build and release through the usual flow (docs/runbooks/mobile-builds.md). The mobile app reads the same household endpoint, so it follows the flag with no separate switch.