Lilith Metaverse · Features

Tara Studio: Instructors, Classes & Coaching

A focused page within the Lilith Metaverse Features documentation. The full map and every sibling page live in the Features hub.

7sections14 minread1diagram2tables

On this page
sequenceDiagram participant Teacher as Verified instructor participant Studio as Tara Studio participant Student as Student client participant Aja as On-device Aja Teacher->>Studio: Publish credentialed class plan Studio-->>Student: Offer schedule and consent terms Student->>Studio: Join with explicit permissions Studio-->>Teacher: Admit eligible participant Student->>Aja: Provide local camera frames Aja-->>Student: Private pose cue Note over Student,Aja: Frames and body landmarks stay on device Teacher->>Studio: Close class and record governed outcome

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 V3InstructorProfile and V3InstructorCredential Prisma models (libs/v3/tara-studio/prisma/schema.prisma), and evaluateTaraGaInstructorInventory() (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.md narrates — the applied → credentials-verified → audition-passed → listed onboarding 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 in lilith-world-server and the V3Mode_TaraLiveClass UE 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-motion is 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 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-onlysrc/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.