Log in seconds
A parent may be holding a child in a dark room. One tap should start a record, and a poor connection should not lose it.
Parents record sleep events on a phone. Coaches review structured timelines in a web dashboard. Supabase keeps each family's records separate.
Verified against the repository on 1 September 2026
Parent PWA
Coach dashboard
Supabase backend
A parent may be holding a child in a dark room. One tap should start a record, and a poor connection should not lose it.
Assigned client records appear in one dashboard, ready for a morning review or a weekly coaching call.
The MVP aims for one paying coach, two active families per coach, and at least five logged nights each week.
Owns a child profile and its sleep logs. Uses the installable phone app.
Invites families, reads assigned records, and manages displayed branding.
Creates coaches, changes account state, and reassigns families.
Auth, sleep logs, history, child profile, install prompt, offline create queue.
Clients, timelines, invitations, branding, coach lifecycle, reassignment.
Sends coach welcome messages and parent invitation links.
Ordinary records go straight from the browser to Supabase. Row-level security is the real permission boundary. Edge Functions handle account operations that need the secret service-role key.
send-welcome verifies the bearer token and requires the admin role.Branding and email failures do not fail the request. There is no password-change screen and no rollback after a partial account creation.
Owners: CreateCoachModal.tsx → send-welcome/index.ts → handle_new_user()
send-invite requires an active coach and rejects an existing profile./signup?token=... and asks Resend to send it.The backend returns success after a Resend failure, and the dashboard never turns on its email-failed message.
The function requires an email field, but the parent form does not ask for or send one. The function also trusts a submitted email instead of binding the account to the email already stored on the invite.
Owners: AcceptInviteScreen.tsx → accept-invite/index.ts
A night wakeup belongs to the most recent bedtime's date, even when it starts after midnight. With no bedtime, it falls back to the current date.
Only new log creation queues. Reads, edits, closes, and deletes still need Supabase. A lost successful response can leave one UUID stuck in the queue.
The coach screens are read-only for sleep data. Postgres RLS grants access only through the assigned parent's coach_id.
Deactivation sends one request to disable the coach and another to set a 30-day grace timestamp on assigned parents. Reactivation reverses both writes. Reassignment changes a parent's coach_id, which immediately changes which coach RLS allows to read the family.
The parent app and RLS do not enforce read-only access or expiry. The two admin writes are not atomic and use the administrator's browser clock.
Email and password work, but the guard does not require the parent role or load the profile on every restored session.
Parent PWAThe form omits the email required by the public acceptance function, so onboarding stops at validation.
Parent PWA + Edge FunctionWake, nap, bedtime, and night-wakeup buttons create a record with the current time.
Parent PWAThe router imports create and edit screens that are absent, which prevents the parent app from building.
Parent PWAShows open and closed events, elapsed time, quick close, and total closed sleep minutes.
Parent PWACalendar totals, 20-day list windows, day detail, and links to individual records.
Parent PWALoads the family's first child and updates name and birth date.
Parent PWANew records queue in IndexedDB and replay on reconnect. Other actions remain online-only.
Parent PWAGenerated manifest, static asset cache, Android prompt, and manual iOS instructions.
Parent PWAAssigned parents and pending invites appear as client cards with the latest log date.
DashboardSeven-day date range, logical-day grouping, notes, duration, and open-state display.
DashboardLink creation and resend work. Email delivery failure is hidden from the coach.
Dashboard + Edge FunctionName and logo paths exist. The repository does not create Storage object upload policies.
Dashboard + Parent PWAAdmin flow works, but later failures do not roll back the Auth user and there is no password-change screen.
Admin + Edge FunctionAdmin changes coach_id; RLS then transfers coach visibility.
Active state and grace timestamps can be changed, but the operation is split across requests.
AdminNo read-only policy, expiry task, or access cutoff uses the stored grace timestamp.
Backend + Parent PWANo product events, metric dashboards, automated tests, or continuous integration workflow.
OperationsOne Auth user. Role, coach ownership, active state, and grace timestamp.
Belongs to a parent. Schema allows many, screens use the first one.
Type, status, logical date, times, and optional notes.
Coach, family label, email, token, status, and seven-day expiry.
One display name and optional public logo per coach.
| Rule | Current owner | Strength |
|---|---|---|
| One wake per child and logical date | Parent service count before insert | Client only |
| One bedtime per child and logical date | Parent service count before insert | Client only |
| Wake has no asleep or end time | Parent service normalization | Client only |
| Times remain in start, asleep, end order | Parent service validation | Client only |
| Parent changes only their own child and logs | Postgres row-level security | Database |
| Coach reads only assigned families | Postgres row-level security | Database |
| Invite expires after seven days | Acceptance Edge Function | Server |
| Grace makes a parent read-only | No current owner | Not enforced |
FR-P01Sign in with email and passwordPartialFR-P02Create an account from a valid inviteBrokenFR-P03Start any of four log types quicklyReadyFR-P04Create a past or detailed recordMissingFR-P05Edit or delete a recordMissingFR-P06Close an open record in one tapPartialFR-P07Review current and historical recordsReadyFR-P08Replay a new offline record laterPartialFR-P09Use read-only access during graceMissingFR-C01Sign in and see assigned clientsReadyFR-C02Inspect client logs by date rangeReadyFR-C03Invite a parent with a shareable linkPartialFR-C04Update display name and logoPartialFR-A01Create a coach accountPartialFR-A02Activate or deactivate one consistent account statePartialFR-A03Reassign a parent between coachesReadyFR-S01Block unrelated tenant readsUntestedFR-S02Protect ownership and lifecycle fieldsFailedFR-O01Observe failures and product goalsMissingSupabase Auth persists email-and-password sessions. Route guards steer users, but navigation checks do not protect data.
All five public tables enable RLS. Parent and coach policies follow user ID, role metadata, child ownership, and coach assignment.
Edge Functions use a secret key for privileged work. It bypasses RLS and never appears in browser source.
A user cannot change their role, but the broad self-update policy still permits changes to coach_id, is_active, and the grace timestamp.
The public acceptance function should use the invite's stored email. It currently expects an untrusted body email.
Every Edge Function responds with Access-Control-Allow-Origin: *.
Use the invite's stored email and make parent onboarding one recoverable operation.
The missing modules prevent the parent production build.
Database constraints or a transaction-owned function must protect duplicates, time order, and grace behavior.
Users should not be able to change coach ownership or account-lifecycle fields.
The public logo bucket exists, but browser upload authorization does not.
Coach creation, parent onboarding, and deactivation can leave half-finished state.
No automated suite proves RLS, role routing, invitations, queue retries, or grace.
Parents cannot see stuck offline items, and coaches cannot see failed invitation email.
The product goals have no event collection or internal health view.
Complete legal placeholders, backups, alerts, secrets, domains, and rollback steps.
sleep_coach_platform/
├── apps/
│ ├── parent/ installable PWA
│ └── dashboard/ coach + admin
├── packages/shared/ types + client
├── supabase/
│ ├── functions/ privileged flows
│ └── migrations/ schema + RLS
├── docs/ product reference
└── legal/ draft policies
apps/parent/src/main.tsxapps/parent/src/services/sleep-log.service.tsapps/parent/src/services/sync.service.tsapps/dashboard/src/routes/index.tsxapps/dashboard/src/features/admin/AdminCoachDetailScreen.tsxpackages/shared/src/index.tssupabase/migrations/supabase/functions/Use the checked-in source and migrations to answer "what happens now." Use the PRD to answer "what was intended." This handbook keeps the two separate.
The foundation is sensible: separate apps, shared contracts, and database-level tenant isolation. The next work is less glamorous and more important. Finish onboarding, enforce the rules, and prove recovery before a tired parent depends on it at 3am.
Read again from the top