The sequence exposes the privacy boundary: Studio coordinates identity, eligibility, schedule, and consent, while Aja processes the student's body data locally. The instructor receives only the class-level information permitted by the student's explicit choices.
Tara Studio is V3's contemplative-instruction tenant — the place inside the
Lilith metaverse where a verified human yoga teacher leads a room of embodied
students through an authored asana sequence, and where each student's real
body is coached from a webcam whose footage never leaves their device. Tara is
the one V3 loop that grades a person rather than pixels or text, and that
changes the burden of proof: where a V2 combat realm had to be provably
deterministic, Tara has to be provably safe and consent-shaped. An
instructor's avatar may not close its eyes or touch a student without explicit
permission; an unverified teacher may not be scheduled for a class that admits
minors; a student's pose data may not be shipped off the laptop. This page
covers the three features that carry that promise: how an instructor is
onboarded and verified into the studio, how a live class session is
scheduled and governed end to end, and how Aja body-aware coaching turns a
camera frame into a spoken alignment cue without ever transmitting the frame.
The data, policy, and pose-math tier behind all of it lives in three libs/v3/
packages — @oshun/tenant-tara-studio (libs/v3/tara-studio),
@oshun/aja-pose (libs/v3/aja-pose), and @oshun/isis-motion
(libs/v3/isis-motion); the in-world rendering and room runtime are the UE5
client and world server documented in the architecture companion. The section
hub is ../V3_features.md.
What ships, honestly#
Tara's hardest-to-fake parts are real, tested TypeScript; its room runtime and its accuracy numbers carry honest caveats. Here is the split before the details.
- Verification is a real launch gate over a real data shape. Instructor
identity persists through the
V3InstructorProfileandV3InstructorCredentialPrisma models (libs/v3/tara-studio/prisma/schema.prisma), andevaluateTaraGaInstructorInventory()(ga-inventory.ts:123) is an all-or-nothing readiness gate that fails unless twelve seeded instructors are each verified, onboarded, audition-approved, insured, and carrying a published four-week schedule. - The in-class consent stack is real, fail-loud code — five independent policies (eyes-open, physical-adjustment, invitational-language linter, class-style mode, lineage grounding), each a small decision engine with an audit event, across 16 spec suites (69 cases).
- Aja is a full on-device pipeline, not a wrapper: a MediaPipe→MoveNet fallback cascade, an 18-feature biomechanical extractor, a nearest-centroid classifier over 30 canonical asanas, ten alignment-cue rules, six known-risk modification ladders, and a recursive raw-pose egress blocker — 21 automation specs.
- The honest caveats. First, the lifecycle state machines that
V3_features.mdnarrates — theapplied → credentials-verified → audition-passed → listedonboarding flow and the pre-class-lobby → opening → close session flow — are product narrative plus a persisted data shape, not a runtime FSM in this library; the class runtime (room spawn, the 90-second instructor-disconnect grace hold) lives inlilith-world-serverand theV3Mode_TaraLiveClassUE plugin. Second, Aja's "≥ 92 % per-asana accuracy" and "≥ 90 % cue relevance" gates are computed against a deterministic synthetic corpus (buildCanonicalAsanaTrainingCorpus,canonicalAsanaPrototype), so they are self-consistency proofs of the algorithm, not field accuracy. Third, the pose models and the TTS voice are runtime/provider-gated behind dynamic imports and an injected adapter. Fourth,@oshun/isis-motionis a descriptor-only placeholder. Each section below says where its claim is backed.
Instructor onboarding and verification#
The data shape behind a verified teacher#
Before any lifecycle, there is a record. V3InstructorProfile
(schema.prisma:90) carries credentials, insurance, lineageAttestation,
and traumaInformedTraining as structured JSON, a backgroundCheckStatus
string, an avatarBinding, and the v1UserId tying the studio identity back to
the member's V1 account. V3InstructorCredential (schema.prisma:110) is one
row per credential — credentialType, issuingOrg, issuedAt, an optional
expiresAt (so an expired RYT is detectable), and a yogaAllianceLookup blob
recording the registry-API result where one exists. Those two models are the
durable spine the gates below decorate.
The gates, mapped to the fields#
V3_features.md defines a staged flow with six gates; each maps onto the data
shape above:
| Gate | What it checks | Where it lands |
|---|---|---|
| Credential check | Yoga Alliance RYT-200/500, E-RYT, Ayurveda/somatic certs; registry API lookup plus operator confirmation | V3InstructorCredential.yogaAllianceLookup + credentialType |
| Lineage attestation | a named lineage (Iyengar / Ashtanga / Kripalu / Yin / …) and a teacher attribution; an unverifiable attribution lists as "self-attested" rather than blocking | V3InstructorProfile.lineageAttestation; grounded by Sophia (see live-class stack) |
| Trauma-informed training | recommended, not required; earns a badge and is required to teach prenatal / recovery / grief-adjacent classes |
V3InstructorProfile.traumaInformedTraining |
| Background check | required before scheduling any class that admits minors; blocked at the scheduling step, not the room gate | V3InstructorProfile.backgroundCheckStatus |
| Live audition | one 30-minute observed class scored on tone, cueing clarity, safety, and platform fluency; one re-audition after 30 days | auditionStatus in the GA inventory record |
| Liability insurance | required to list any live class above 25 seats; carrier/policy/expiry stored | V3InstructorProfile.insurance |
The honest line: the transition machine between applied,
credentials-verified, audition-passed, and listed is described in the
product spec and run by the Tara editorial pool at a 5-business-day SLA; the
repository ships the persisted shape those transitions write to, not an
executable state graph.
The launch gate that proves the cohort — real code#
What is executable is the readiness gate over the whole launch cohort.
createTaraGaVerifiedInstructorInventory() (ga-inventory.ts:97) seeds twelve
named instructors (TARA_GA_REQUIRED_VERIFIED_INSTRUCTOR_COUNT = 12), each
generated with four published catalog classes and a four-week recurring schedule
(FREQ=WEEKLY;COUNT=4). summarizeTaraGaInstructor() (ga-inventory.ts:198)
reduces each instructor to a readiness summary — verified,
onboardingComplete, auditionApproved, catalogPublished, activeInsurance,
and a fourWeekRecurringSchedulePublished flag computed by actually counting
four published classes in each of four weeks — and
evaluateTaraGaInstructorInventory() (ga-inventory.ts:123) refuses to pass
unless every instructor is ready, the count is exactly twelve, and there are
no duplicates. This is the bright line between "we have a yoga tenant" and "we
have a yoga tenant with twelve real, fully-credentialed, fully-scheduled
teachers": it is a gate that fails loud with a per-instructor error list, not a
checkbox.
Verified instructors as the root of trust#
Verification is not just a profile badge; it is a capability. Tara permits AI
instructor personas, but registerTaraAiPersonaSponsor()
(ai-persona-sponsor.ts:107) throws
Only verified human instructors may sponsor a Tara AI persona (:182) unless
the sponsor's verificationState === 'verified' and they carry credential
references. And evaluateTaraAiPersonaRegionalSchedulingCap()
(ai-persona-region-cap.ts:50) ties persona supply to human supply by floor
division: Math.floor(verifiedHumanInstructorCount / 4) personas per region, so
a region with three verified humans gets zero personas. The verified human is
the unit of trust the whole persona economy is rationed against — personas,
voice contracts, and economics get their own deep treatment in
./tara-authoring-personas-tone-economics.md.
A listed instructor's verified badge also renders on the avatar nameplate at the
close LOD bands — see
./avatars-nameplates-spatial-audio.md.
Live class sessions#
What a class is, and where it runs#
A live class is a scheduled, instructor-led session in a class-tier room
(capacity ≤ 64). It persists as V3LiveClassSession (schema.prisma:13), whose
columns are exactly the things an instructor fixes at schedule time: startsAt
/ endsAt, capacity, instructorId, sequenceId (the authored asana
sequence), roomId, venueId, recordingEnabled, attendance, and a
tonePolicyMode. The authored sequence itself is V3AsanaSequence
(schema.prisma:51) — asana refs, breathwork/meditation blocks, transitions,
prop prompts, a lineage tag, difficulty and pace — and per-student coaching
events are V3AjaCueEvent (schema.prisma:127), each row carrying the
cueText, riskFlag, modificationLadderStep, and a poseErrorVector.
The runtime that walks a class through pre-class lobby, opening, practice body,
and close — including the rule that an instructor disconnect beyond 90 seconds
converts the session to guided on-demand playback rather than stranding students
— is the world-server + UE pipeline: schedule → booking (V1 BFF) → room spawn
(lilith-world-server) → UE clients load V3Mode_TaraLiveClass while Tier-2
fallback clients render the three.js scene. tara-studio owns the contract
and policy tier under that runtime; the architecture companion
../architecture/tara-classes-aja-and-commons.md
traces the full spawn-and-cue-fan-out path.
The consent and safety stack that governs the room — real code#
The heart of the tenant is five independent gates, each a small decision engine that emits an audit event:
| Policy | File | Rule |
|---|---|---|
| Eyes-open default | eyes-open-policy.ts |
Avatar eyes stay open; an eyes-closed cue is suppressed unless the invitation text matches an explicitly permissive pattern |
| No-surprise contact | physical-adjustment-consent.ts |
Default hands-off; in-world contact is dialog-required/blocked until the student grants a dialog-matched consent record |
| Invitational language | invitational-language-linter.ts |
13 directive phrases flagged blocking, forcing needs-editorial-revision before a script ships |
| Class style mode | class-style-mode.ts |
Directive cueing is allowed only in an explicitly-declared advanced-directive-opt-in class, surfaced on the listing |
| Lineage grounding | lineage-grounding.ts |
A lineage claim needs a cited, supporting source or it is marked needs-citation and blocks editorial review |
These are not config tables. evaluateTaraSequenceEyeState()
(eyes-open-policy.ts:43) suppresses an eyes-closed cue to eyes-open with
autoCloseSuppressed: true unless explicitInvitation is set and the
invitation text matches one of EXPLICIT_EYES_CLOSED_INVITATION_PATTERNS
(:36: "if it feels", "you may", "optional", "invitation to close") — and the
same default is mirrored by the C++ V3TaraEyesOpenPolicy on the avatar runtime
side, so authoring and rendering cannot disagree about whether a teacher's eyes
may close. evaluateTaraPhysicalAdjustmentConsent()
(physical-adjustment-consent.ts:85) returns dialog-required when there is no
consent record, blocked when the record's dialogId or studentUserId does
not match the attempt, and authorized only on an explicit granted record —
contact is off by default and stays off on any mismatch.
lintTaraInvitationalLanguageSequenceScript()
(invitational-language-linter.ts:56) walks every cue line against
DIRECTIVE_LANGUAGE_RULES (:40) and flips a single
must/force/push/hold to needs-editorial-revision with a per-issue
replacement suggestion. The lineage grounder,
buildTaraLineageEditorialReview() (lineage-grounding.ts:204), is a genuine
little reasoning engine: it resolves each claim's cited source IDs against a
registry with authority scores, checks the source actually supports the claimed
lineage tag, and blocks the review on the fixture's uncited "direct Mysore
family line from 1932" claim.
The asana sequence and its Aja cue bundles#
A class teaches a sequence drawn from TARA_CANONICAL_ASANA_LIBRARY
(asana-library.ts:234). The library is built combinatorially as 30 base asanas
× 10 lineage framings = 300 entries (TARA_ASANA_LIBRARY_GA_MINIMUM), and
validateTaraCanonicalAsanaLibrary() (asana-library.ts:240) is another
all-or-nothing GA gate: every entry must be editorial-signed, design-approved,
and carry ≥ 3 lineage variants, ≥ 4 modifications, ≥ 2 family-keyed
contraindications (an inversion warns on neck and hypertension; a backbend on
lumbar and pregnancy), and a ≥ 3-cue Aja bundle. The honest qualification: this
is 30 distinct shapes through ten teaching framings, not 300 distinct shapes —
substantial content with real per-family safety metadata, generated rather than
hand-authored entry by entry.
After class: practice logs, plans, and continuity#
Each session writes a practice log to the participant's V1 Arete record, and the
broader arc is a Tara practice plan. completeTaraGuidedOnboarding()
(practice-plan.ts:170) maps a 5-minute intake
(TARA_GUIDED_ONBOARDING_DURATION_SECONDS) of goal/experience/intensity/time to
the best-scoring starter template (the scorer down-weights crow-prep for a
wrist-sensitive user by 40 points) and emits a validated four-week, 20-session
schedule of on-demand-class and self-practice slots, persisted as
V3PracticePlan (schema.prisma:147) with its areteLinkage.
createTaraFourWeekAdjustmentJourney() (practice-plan-adjustment.ts:66) then
re-tunes the plan from attendance, Aja coverage signals, and Arete weekly-review
energy — proposing add-foundation-class, add-self-practice, or
reduce-weekly-load — mirroring V1 Arete's humane, user-confirmed re-scoping.
Body-aware coaching (Aja)#
Aja is the loop that grades a real human body, and its safety property is that
the camera frame never leaves the device. The pipeline in libs/v3/aja-pose/src
is six honest stages.
The on-device runtime and its fallback#
AjaPosePipeline (runtime.ts:399) is a real fallback cascade. The primary
estimator is the MediaPipe Tasks-Vision pose-landmarker (33 named landmarks);
the fallback is a TensorFlow MoveNet lightning detector (17 keypoints). On any
init-failed/estimate-failed the pipeline advances to the next adapter and
fires onFallback, so a GPU-delegate failure degrades to CPU MoveNet rather
than dropping the student — only when every adapter fails does a frame throw.
Both device profiles (web-m1-mac, mobile-iphone-15-pro) target 15 Hz
(AJA_POSE_MINIMUM_HZ, a 66.7 ms budget), and assertAjaPosePerformanceGate()
(runtime.ts:731) throws if observed Hz or average inference blows it. The
estimators load through a dynamicImport seam, which is also the honest caveat:
the models are runtime-gated, so the geometry unit-tests without a wasm bundle.
Features and classification#
extractAsanaFeatureVector() (classifier.ts:391) turns raw landmarks into 18
rotation- and scale-robust biomechanical features (ASANA_FEATURE_NAMES):
shoulder/hip slope, spine angle, four limb joint angles each computed as
acos(dot / |a||b|), wrist/ankle spans, shoulder–hip twist, and side lengths.
trainAsanaCentroidClassifier() (classifier.ts:239) averages per-asana
vectors into centroids over the 30 canonical asanas (AJA_CANONICAL_30_ASANAS,
Tadasana through Savasana); classifyAsanaFeatureVector() (classifier.ts:345)
returns the nearest centroid by Euclidean distance with a
first-to-second-distance confidence; and evaluateAsanaClassifier()
(classifier.ts:274) builds a confusion matrix and enforces the ≥ 92 %
per-asana gate (AJA_ASANA_VALIDATION_MIN_PER_ASANA_ACCURACY). The extractor
and classifier run on real MediaPipe landmarks at runtime; the corpus they are
graded on is synthesized from sinusoidal prototypes (canonicalAsanaPrototype,
classifier.ts:461), so the 92 % is a proof of the algorithm's separability,
not a field measurement.
Alignment cues, risk flags, and the modification ladder#
Ten rules (AJA_ALIGNMENT_CUE_RULES, cue-generator.ts:93) compare features
against thresholds — absolute-greater-than for a tilted shoulder line,
centroid-deviation for a spine that has drifted from the target asana's
centroid, less-than for a collapsed stance. generateAjaAlignmentCues()
(cue-generator.ts:206) sorts candidates by priority + deviation, slices to
the top N, and, when a classifier model is supplied, decorates each cue with
ML assistance (predicted vs. target asana, distance, max feature deviation). If
nothing triggers but the classifier says the student is in the wrong shape
entirely, it emits one return-to-target cue. Relevance is gated at ≥ 90 %
(AJA_CUE_RELEVANCE_MIN_HUMAN_APPROVAL) against a review set that is, again,
built programmatically.
Risk is a separate, sterner channel. Six rules
(AJA_KNOWN_RISK_MODIFICATION_RULES, risk-flagger.ts:102) cover
shoulderstand, plow, lotus, chaturanga, upward-dog, and tree, each keyed to
anatomical factors (neck, lumbar, knee, wrist, shoulder, balance,
hypertension, pregnancy) and carrying a real three-step modification ladder
with cue text and props ("place a block under your sacrum for supported bridge"
→ "legs up the wall" → "rest in savasana"). A new practitioner trips every
rule for their target asana; an experienced one trips only rules matching
their declared risk factors. routeAjaRiskModification()
(risk-flagger.ts:275) routes within a 250 ms budget
(AJA_RISK_MODIFICATION_BUDGET_MS), and the gate requires every validation case
to route to the correct ladder, on time.
Voice delivery#
Cues are spoken in the instructor's own voice through deliverAjaVoiceCue()
(voice-cue.ts:151), which builds an AjaPsycheTtsCueRequest (Opus,
low-latency, routed psyche-tts → v3voice on a student-private-cue channel)
and measures pose-to-first-audio against a 250 ms budget
(AJA_VOICE_CUE_DELIVERY_BUDGET_MS). The instructor tone profile is validated
(warmth/energy ∈ [0,1], speech rate ∈ [0.75, 1.25]). The TTS provider is an
injected adapter with a deterministic test double, so the gate proves the budget
and the routing, not a live network.
The privacy gate — the load-bearing property#
This is what makes camera coaching defensible. containsAjaRawPoseData()
(privacy.ts:161) recursively walks any egress payload and returns true if it
contains any raw-pose key (image, frame, poses, landmarks,
worldLandmarks, world, keypoints, segmentationMasks).
buildAjaIrisAggregateMetricsEnvelope() (privacy.ts:100) produces an
Iris-bound envelope of counts and averages only, then runs it through
assertAjaNoRawPoseDataLeavesDevice(), which throws if raw data is present.
The only thing that can reach Iris memory is aggregate metrics, and the gate
fails loud rather than leaking silently. Camera estimation is opt-in per session
and never recorded; this code is why that claim holds.
isis-motion: honestly labeled#
@oshun/isis-motion (libs/v3/isis-motion) is descriptor-only —
src/index.ts exports a capability descriptor (motion-brief, retarget-pass,
cue-export) and a readiness scorer, with no implementation behind it. It is
the planned seam for a motion-authoring service between Tara's asana sequences
and the retarget runtime; reading it expecting a motion engine will mislead. The
real motion lives in the V3Avatar retarget/posture code and in the asana
catalog above.
How it connects#
Tara is the consumer of V3's embodiment substrate, not a standalone app. The instructor and student avatars, the verified-instructor nameplate badge, the eyes-open/posture policies on the avatar runtime, and the spatial voice that carries an instructor's cues all come from avatars, nameplates & spatial audio. The AI-persona machinery that verification unlocks — sponsorship, voice contracts, the persona economy, and class tone — is the sibling Tara authoring, personas, tone & economics. And the room-spawn, cue-fan-out, on-demand-library, and Lilith Commons context around a class is mapped in the architecture companion, Tara live classes, Aja coaching & Lilith Commons. The honest line throughout: the verification gate, the consent policies, the asana catalog, the practice-plan generator, and the Aja pose math are real and tested; the class runtime, the pose models, and the TTS voice are the runtime and providers they gate.
Related#
- Tara Authoring, Personas, Tone & Economics — AI instructor personas, voice contracts, and the persona economy that verified humans gate (forthcoming sibling page)
- Avatars, Nameplates & Spatial Audio — the embodiment substrate: the avatar body, the verified-instructor badge, the eyes-open policy on the runtime, and the voice that carries Aja cues
- Tara Live Classes, Aja Coaching & Lilith Commons (architecture) — the class runtime, room spawn, on-demand library, and the full Aja/consent internals
- The section hub: ../V3_features.md