Dance studio platform · Phase 0 discovery · what we have decided, inferred, and parked
Draft v0.2
24 October 2026
Tempo 🩰 · for Craig
DecidedProposedDeferredNot modelled yet
The prototype is throwaway. This is the artifact that survives it: the shapes we think the
real system needs, and the rules that must hold regardless of how we implement them.
1 · Identity, membership, roles
The load-bearing decision: personas are views, not account types. A human is one row.
What they can see comes from relationships and roles, never from a "type".
The triple-role adult — a parent who takes an adult class and also teaches — is one
person, one login, one membership, and three role rows. No duplicate accounts, no "which one am I"
at sign-up.
2 · Events and the calendar
A class is not the only thing on a studio calendar, and a calendar is not a table of rows
someone maintains by hand. Events are typed, may run several days, may belong to several dancers at once,
and may contain their own sub-schedule.
Why generated, not stored: if a studio must re-enter the calendar each week,
it drifts, and every feature built on it inherits the drift.
Why entries exist: "which number, at what time, who's in it" is the actual
question a parent asks. One block per competition cannot answer it.
3 · How permission is derived
Holding teacher + guardian does not let a teacher-parent read another family's billing, nor a
guardian read notes about other students. Grants union; scopes intersect.
4 · Entity dictionary
personDecided
One row per human, studio-agnostic.
id · names · date_of_birthcore
email, phone, photonullable
billing / payer fieldsforbidden
persona / type columnforbidden
Age band is computed from DOB, never stored — birthdays self-heal.
loginDecided
Credentials, one per person across all studios.
person_id1:1
passkey / passwordadult
PIN, guardian-issuedunder 13
emailnot required for kids
Under-13 accounts must never depend on a child's inbox.
membershipDecided
A person's relationship to one studio.
person_id + studio_idunique
statusinvited → active → alumni
started_on / ended_onhistory kept
membership_roleDecided
Roles are rows. A person may hold several.
membership_id + rolePK
owner · admin · teachercatalog
choreographer · student · accompanistcatalog
guardianderived, not granted
guardian_ofDecided
Person → person. Global relationship, per-studio verification.
guardian_person_id→ person
student_person_id→ person
authorityfull · view-only · emergency
verified_at / verified_byper studio
Studio A verifying a guardian must not grant access at studio B.
eventProposed
A dated happening, generated from patterns.
title · type_id · room · teachercore
date … untilmulti-day = 1 row
start · endlocal time
members[]several dancers
Never hand-maintained: pattern + exceptions generate instances.
schedule_ruleProposed
The weekly pattern a studio authors once.
class / levelreference
day_of_week · start · endpattern
room · teacherdefaults
valid_from → valid_toseasonal
exceptionProposed
A dated change to a generated instance.
event_id→ event
new_time / new_room / cancelleddelta
authored_by · reasonaudit
The exception — not the event — is what fires a notification.
entryProposed
A number inside a competition or recital.
event_id · date · time · durown schedule
routine_namedisplay
members[]cast, may be outside family
stage · divisionlogistics
Same shape serves recital order-of-show.
event_typeDecided
Reference table. Studios invent types.
class · practice · rehearsalseed
competition · recital · meetingseed
closuresuppresses attendance
Type drives behaviour (attendance, travel, permissions), not just colour.
billing entityDeferred
Not being built. One rule is being kept alive.
money attaches to a billing entitynot to person
may hold several peoplesplit tuition
per studiotwo schools, two relationships
No payer / balance / discount columns on person or membership, ever.
attendanceNot modelled
Held back deliberately, but it gates a lot.
event_instance + studentshape tbd
present · absent · late · makeupstates tbd
If recording it is slow, every claim about a dancer's week becomes guesswork.
5 · Invariants worth defending
No persona columns.Behaviour comes from relationships and roles. The moment a "type" exists,
the adult student, the teacher-parent, and the grandmother-payer all become exceptions.
Roles are a set; grants union, scopes intersect.Holding two roles never widens what data you
may see beyond the scope of each role.
One human, one login, many studios.Identity is global; authority is per studio.
The calendar is derived.Patterns generate instances; exceptions are the only edits. A
hand-maintained calendar is a calendar that lies.
Events belong to rosters, not to a dancer.A competition with two siblings and three guests is
one event with a cast, not five events.
Type is data, and type drives behaviour.Closure means "don't mark absent". A meeting has no
attendance. A competition has travel.
Money never lives on a person.The only billing decision made so far is where billing is
allowed to attach later.
6 · Open, and honestly unresolved
instances vs definitionsOpen
Until instances are first-class, "this rehearsal moved"
cannot be expressed cleanly or notified reliably.
attendanceOpen
Target: under 15 seconds per class, or the data rots and
everything downstream inherits it.
permission policyOpen
Hooks exist; no policy rows written. Teacher-parent
conflict rules land here, later.
guardian verificationOpen
In-person at the desk, email invite, or both — decides the
onboarding screen count.
closuresOpen
Thanksgiving currently looks like empty days, which reads
as a scheduling mistake rather than "we're closed".
duplicate peopleOpen
Studios already hold duplicates. Merge tool in v1, or accept
them until it hurts?