Lilith Metaverse · Features

Tara Studio: Sequence Authoring, Personas, Tone & Economics

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

7sections13 minread2diagrams1table

On this page

Tara Studio is the yoga-instructor tenant of the Lilith Metaverse, and this page covers the part a teacher actually does before anyone enters a room: build a sequence out of a canonical asana library, optionally stand up a supervised AI persona to extend their reach, stay inside a trauma-informed tone and lineage policy, and get paid for it — including for their cloned voice. Where the avatars and spatial-audio page treats embodiment and the world page treats presence, Tara has a different burden of proof. It is not "is this deterministic?" but "is this consent-shaped and provably attributable?" — an instructor's avatar must not close its eyes or touch a student without permission, a persona must not teach an asana its human sponsor never approved, a lineage claim must carry a citation, and a voice signature must never leave the surface it was licensed for. Every one of those is a state machine or a fail-loud gate in real, test-covered TypeScript under libs/v3/tara-studio (the @oshun/tenant-tara-studio package), with the grounding engine shared from libs/v3/sophia-saraswati-grounding. The running-a-class and coaching side is its sibling, Tara instructors, classes & coaching; the money mechanics it shares with concerts live in commerce, rights & takedown; the deep engine/pipeline treatment is the architecture companion, ../architecture/tara-classes-aja-and-commons.md. For the full feature scope this slots into, start at the hub: ../V3_features.md.

What ships, honestly#

The split is worth stating up front, because the monolith describes a richer product than the code implements, and this page follows the code.

  • Real and test-covered. @oshun/tenant-tara-studio is substantive, domain-specific TypeScript — 19 source modules, 16 spec suites (~69 it cases), a Prisma persistence layer, and a 21-entry capability ledger (v3TaraStudioDescriptor, src/index.ts:17). The asana library, the lineage grounder, the invitational linter, the eyes-open/style-mode policies, the four AI-persona gates, and the five-file TTS-voice economics stack are all genuine, deterministic, and covered. The Saraswati-side grounder in libs/v3/sophia-saraswati-grounding (8 specs) is the structural twin of Tara's lineage grounder and the citation companion this page cross-references.
  • "300 asanas" is combinatorial. TARA_CANONICAL_ASANA_LIBRARY is 30 base seeds × 10 lineage framings = 300 entries generated by flatMap (asana-library.ts:234), not 300 distinct shapes. Each entry carries real per-family contraindications, four modifications, and a three-cue Aja bundle — substantial content, honestly not 300 unique poses.
  • Grounding is deterministic, not an LLM. Both groundLineageClaim (lineage-grounding.ts:316) and reviewSaraswatiBackstoryGrounding (backstory-grounding.ts:504) check claims against a static source registry with authority scores. They are genuine little reasoning engines; they are not live model calls.
  • The data/policy tier, not the editor UI. The drag-and-drop asana editor, the stand-in-avatar timing preview, and the in-world cue rendering are the Next.js web Studio (apps/oshun/web/src/app/lilith-studio/asana/) and the UE5 client. This library owns the asana data, the consent/governance policies, the AI-persona contracts, and the voice economics — the contract behind those surfaces, not the surfaces themselves.
  • Economics here = the voice royalty, not the class split. The code implements the instructor TTS voice-signature royalty (a 5% added share). The 20/80 class-fee split, the 10/90 tip/donation rate, and the Tara Lineage Fund described in the monolith are settled in V1 Lilith-Commerce / Aje and are not in this library. The sections below say which is which.

Sequence and asana authoring#

The canonical asana library#

Authoring starts from a catalog, and the catalog is the flagship of the package. TARA_CANONICAL_ASANA_LIBRARY (asana-library.ts:234) crosses 30 seeds — Tadasana through Savasana, each typed with a family (standing, backbend, inversion, arm-balance, restorative, …) and a difficulty — against ten lineage framings (iyengar-supported, ashtanga-vinyasa, krishnamacharya-viniyoga, yin-restorative, trauma-informed, chair-accessible, senior-mobility, …). Every one of the 300 resulting TaraCanonicalAsana entries (buildAsanaEntry, :297) carries three lineage teaching notes, four modifications with real cue text and props (a prop is chosen by family — block for backbends/inversions, wall for standing/balance, strap for core/twist), family-keyed contraindications, a three-cue Aja alignment bundle, an Isis editorial signature, and a Tara design-council review.

The contraindications are the part that earns trust, because they are not generic "consult a doctor" boilerplate — contraindicationsFor (:383) keys a real clinical table off the asana's family: an inversion warns neck-injury (severity avoid) and hypertension (caution); a backbend warns lumbar-compression (caution) and pregnancy-compression (avoid); a forward-fold warns hamstring-strain and disc-irritation. That is exactly the data the monolith promises a sequence will surface in its listing and use to gate minor cohorts.

The library is GA-gated all-or-nothing. validateTaraCanonicalAsanaLibrary (:240) counts entries that are signed, design-approved, and carry ≥3 lineage variants, ≥4 modifications, ≥2 contraindications, and a ≥3-cue bundle, and passesGaGate is true only when every count equals the library length and the length is ≥ 300 (TARA_ASANA_LIBRARY_GA_MINIMUM). assertTaraCanonicalAsanaLibraryGaGate throws otherwise — one editorially-uns- igned asana fails the whole gate, which is the point.

From asana to sequence — and to a plan#

An instructor authors a sequence entirely on the web (no UE install): drag asanas from this library, insert breathwork/meditation/transition/prop blocks, tag tradition/pace/difficulty/contraindications, preview pacing on a stand-in avatar, and publish as a live-class template or on-demand class through the V1 environment-promotion gates. The library above is what populates that palette; the published artifact is an AsanaSequence manifest the UE5 client and the Tier-2 fallback bind at runtime, persisted through the V3AsanaSequence / V3Asana Prisma models whose payloadHash column makes the shipped JSON provable against its row (see the content-pipeline companion).

The same "ordered sequence of asanas with durations" shape also drives guided practice plans: completeTaraGuidedOnboarding (practice-plan.ts:170) scores a 5-minute intake against a starter-template library (scoreStarterTemplate weights goal-match at 60, demotes crow-prep by 40 for a wrist-sensitive practitioner) and emits a validated 4-week / 20-session schedule. Plans are a student-continuity feature, so they live mostly on the coaching sibling; the authoring takeaway is that one canonical asana vocabulary feeds class sequences, AI-persona scripts, and practice plans alike.

Supervised AI instructor personas#

Tara permits a small number of supervised AI personas to cover off-hours, localized, and beginner classes. The monolith's promise — "sponsored by a human, teaches only approved scripts, clearly labeled, rate-limited" — is implemented as four independent gates, each a real function with an audit event.

Sponsorship requires a verified human#

registerTaraAiPersonaSponsor (ai-persona-sponsor.ts:107) refuses to create a persona unless validateSponsorInput (:181) confirms the sponsor's verificationState === 'verified' and that they carry credential references; the persona is then bound to a closed scope set drawn from TARA_AI_PERSONA_SCOPE_OPTIONS (sequence-script-only, student-q-and-a, post-class-reflection, booking-prep, safety-handoff). A sponsor can disableTaraAiPersonaSponsor (:134) with a reason (≥ 8 chars) that appends a disable audit event — editorial ownership is revocable and logged.

The region cap is floor division#

evaluateTaraAiPersonaRegionalSchedulingCap (ai-persona-region-cap.ts:50) enforces one AI persona per four verified human instructors by Math.floor(verifiedHumanInstructorCount / 4). The fixtures make the arithmetic honest: a region with 3 verified humans (US-NY) gets floor(3/4) = 0 personas; 8 humans with 2 active personas (US-CA) is capReached. Verified-human schedules never consume the cap, and an AI-persona attempt with no aiPersonaId throws. There is no way to schedule the (N+1)th persona without onboarding four more humans first.

Out-of-script asanas hand off or no-op#

A persona teaches only from a sponsor-approved script. authorTaraAiPersonaSeq- uenceScript (ai-persona-sequence-script.ts:124) requires an active sponsor registration carrying sequence-script-only scope, validates every line's asana against the canonical library (assertKnownCanonicalAsana, :259), and runs the script lines through the invitational linter below. At runtime, evaluateTaraAiPersonaSequenceAttempt (:154) matches a requested asana against the script: a scripted asana returns speak-script-line; anything else returns handoff-to-sponsor or graceful-noop per the script's restrictionMode — never an improvised pose. The fixtures encode exactly the monolith's adversarial case: the in-script tadasana-iyengar-supported is permitted; the out-of-script "improvise Lotus as a surprise peak pose" (padmasana-iyengar-supported) is blocked.

flowchart TD A["AI persona asana attempt"] --> K{"known canonical asana?"} K -->|no| Err["throw — unknown asana"] K -->|yes| M{"in sponsor-approved script?"} M -->|yes| Speak["speak-script-line (permitted)"] M -->|no| R{"restrictionMode"} R -->|handoff-to-sponsor| H["handoff-to-sponsor (blocked)"] R -->|graceful-noop| N["graceful-noop (blocked)"]

The nameplate label can't come off#

buildTaraAiPersonaSponsorNameplate (ai-persona-nameplate.ts:29) stamps a locked, removable: false label reading AI persona — sponsored by <sponsorDisplayName>, and validateTaraAiPersonaNameplate throws if the text is edited or the lock cleared. blockTaraAiPersonaNameplateRemoval (:51) flips removalBlocked and audit-logs any removal attempt rather than honoring it — the "sponsored by a human" disclosure is structurally non-removable.

Tone, lineage, and citation#

Tara's trauma-informed defaults are "enforced by policy rather than left to instructor discretion," and the policy is four cooperating gates.

Gate File Rule
Invitational language invitational-language-linter.ts 13 directive phrases flagged blocking; any hit forces needs-editorial-revision
Eyes-open default eyes-open-policy.ts Eyes stay open; a close cue is suppressed without an explicit, pattern-matched invitation
Class style mode class-style-mode.ts Directive cueing only in a declared advanced-directive-opt-in class, surfaced in the listing
Lineage grounding lineage-grounding.ts An uncited lineage claim is needs-citation and blocks editorial review

Invitational language. lintTaraInvitationalLanguageSequenceScript (:56) runs each cue line against DIRECTIVE_LANGUAGE_RULES (:40) — 13 word-boundary, case-insensitive patterns (must, should, need to, have to, do not, don't, never, force, push, hold, stand, move, teach) — each with a concrete rewrite suggestion ("Replace 'must' with 'you might' or 'if it works for you'"). Every match is emitted at blocking severity, so a single must or force flips the whole review to needs-editorial-revision, and assertTaraInvitationalLanguageReviewApproved (:87) throws on it. This is the same linter the AI-persona script author calls, so personas can't smuggle directive language in either.

Eyes-open and style mode. evaluateTaraSequenceEyeState (eyes-open-policy.ts:43) defaults every cue to eyes-open; an eyes-closed request is suppressed (applied state forced back to open, autoCloseSuppressed: true) unless the cue carries an explicitInvitation flag and the invitation text matches one of four permissive patterns (EXPLICIT_EYES_CLOSED_INVITATION_PATTERNS — "if it feels…", "you may…", "optional…", "invitation to close"). This TS policy deliberately mirrors the C++ runtime V3TaraEyesOpenPolicy so authoring and the avatar can't disagree about whether an instructor's eyes may close. class-style-mode.ts then lets an instructor opt a hard class into advanced-directive-opt-in (the only mode where directiveCueingAllowed is true), and composeTaraClassDescriptionWithStyleMode (:55) appends the mode declaration to the listing so students self-select before booking.

Lineage grounding. Every published sequence carries a lineage tag, and Sophia grounds it. buildTaraLineageEditorialReview (lineage-grounding.ts:204) walks each claim through groundLineageClaim (:316) against TARA_LINEAGE_SOURCE_REGISTRY (:103), where each source has a sourceType, lineageTags, and an authorityScore (the verified Iyengar registry profile scores 0.96; the Light on Yoga bibliographic card 0.91). A claim with requiresCitation and no sources resolves needs-citation at blocker severity; cited-but-unsupported resolves needs-citation at major; an editorial-context claim that doesn't require citation passes as "does not publish as factual lineage." The review's status is blocked if any claim needs citation. The fixture is precise: Maya Anand's "verified Iyengar-aligned lineage" and "Iyengar-method vocabulary" claims pass on their sources, but the uncited "direct Mysore family teaching line from 1932" (TARA_LINEAGE_UNSOURCED_CLAIM_ID) is exactly what blocks the review — the unsourced "tradition self-attested" path the monolith describes.

flowchart LR claim["lineage claim"] --> req{"requiresCitation?"} req -->|no| ctx["supported — editorial context only"] req -->|yes| cited{"any cited sources?"} cited -->|none| block1["needs-citation (blocker)"] cited -->|yes| sup{"a source supports the tag?"} sup -->|unknown source id| block2["needs-citation (blocker)"] sup -->|cited, none support| block3["needs-citation (major)"] sup -->|yes| ok["supported"]

This is the same grounding posture Saraswati applies to persona backstory, and the engine is genuinely shared. libs/v3/sophia-saraswati-grounding grounds the six GA persona dossiers (reviewSaraswatiBackstoryGrounding, backstory-grounding.ts:504): every claim that touchesRealCulture — a raga reference, a Sufi-poetry boundary, a Mandarin-language fragment — must carry a Sophia citation trail whose source supports both the persona and the claim kind, or the report is blocked; buildSaraswatiAudienceBackstoryCitationClaims (:597) then renders those trails as hover/tap/gaze citation markers across web, mobile, and VR. It is the Saraswati side of the same "no uncited cultural claim ships" rule that gates a Tara lineage tag.

Instructor economics#

Two economies meet at Tara, and only one of them is in this library.

The class-fee split is the monolith's, settled in V1. The default 20% platform / 80% instructor on paid classes, 10% / 90% on tips and donations, the donation-floor fee waiver, the 10% private fee, and the optional 0–10% routing to a Tara Lineage Fund are the product model in features§"Compensation, Tips, and Studio Splits." They are encoded as an Aje royalty waterfall and settled through V1 Lilith-Commerce / Stripe Connect — not implemented in libs/v3/tara-studio. Treat them as the commerce posture, covered on commerce, rights & takedown.

The voice-signature royalty is real and lives here. When an instructor lends their cloned voice, a separate, mandatory 5% added royalty share rides on top of their normal split — and that machinery is code, as a five-stage stack:

  1. Consent. createTaraTtsVoiceConsentWorkspaceState (tts-voice-consent.ts:106) keeps two separate purpose records (aja-cues, localized-dubs), verified-instructor-only, each Lilith-Rights audit-logged, with a 30-day withdrawal deadline computed on opt-out.
  2. Contract. createTaraTtsVoiceContractTemplate (tts-voice-contract.ts:173) bakes the boundaries in as throwing invariants: Tara-surface-only, a mandatory defaultAddedRoyaltyShareBasisPoints = 500 (5%), a 30-day withdrawal right, Lilith-Rights counsel signoff, and a prohibitedUses list that forbids cross-instructor, cross-tenant, Saraswati-persona, non-Tara-surface, arbitrary-prompt, and public-figure-impersonation reuse. A plain-language summary is surfaced in V1 Studio at /studio/tara/tts-voice-contract.
  3. Recording. reviewTaraTtsVoiceRecordingSession (tts-voice-recording-session.ts:279) requires ≥ 60 clean source minutes (≥ 240 for a studio-grade upgrade), full coverage (asana / breath / Sanskrit / localized-dub / phoneme), a 35 dB SNR floor with zero clipped samples, and encrypted-master-only egress (raw samples stay in Lilith-Rights). Its anti-impersonation gate blocks any voiceprint scoring ≥ 8600 bps (86%) against a public-figure registry and caps the similarity false-positive rate at 1%; only then does buildTaraTtsVoiceSignatureFromRecordingSession mint the voice-signature:tara:<instructor>:v1.
  4. Runtime scope-lock. enforceTaraTtsVoiceRuntimeScopeLock (tts-voice-scope-lock.ts:97) re-checks every playback. detectScopeViolations flags cross-instructor, cross-tenant, saraswati-persona, non-tara-surface, missing-active-consent, or signature-owner-mismatch; on any violation it fails loud — auto-withdraws all active consents, stamps a 30-day withdrawal deadline, and raises a critical Lilith-Rights operator alert. The misuse drill (runTaraTtsVoiceScopeMisuseDrill) proves three attack scenarios all block and auto-withdraw end-to-end.
  5. Settlement. settleTaraTtsVoiceRoyaltyDistribution (tts-voice-royalty-distribution.ts:201) pays the 5% added share only on the two payable use cases — on-demand-aja-cue-playback and localized-dub-stream — and explicitly not on live-class cues. runTaraTtsVoiceRoyaltySettlement- Test is the worked example: $120.00 of on-demand playback yields 600¢, $83.00 of localized dub yields 415¢, for a 1,015¢ added share routed to the signature-owning instructor through an Aje waterfall ref and audit-logged to Lilith-Rights. settle… throws if the review is blocked.

Edge cases and failure modes#

  • One unsigned asana fails the whole library. The GA gate is all-or-nothing equality, not a percentage.
  • A single directive word fails the script. must / force / push flip the linter to needs-editorial-revision; the AI-persona author runs the same linter.
  • Eyes-closed needs words, in two places. The TS Studio policy and the C++ avatar policy both suppress an auto eye-close cue without an explicitly permissive invitation.
  • Personas are rate-limited by humans. Fewer than four verified humans in a region means zero personas, full stop — floor division, no rounding up.
  • An uncited lineage (or cultural) claim blocks publish. Both grounders fail loud on a real-attribution claim with no supporting source.
  • Voice misuse auto-revokes. Any out-of-scope playback request withdraws all active consents and alerts an operator, rather than silently serving the voice.
  • The 5% share is voice-only. It never applies to live-class TTS or the general class-fee split — that economy is V1 Lilith-Commerce.

Where this connects#