# Tara Studio: Sequence Authoring, Personas, Tone & Economics

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](./avatars-nameplates-spatial-audio.md) treats
embodiment and the [world page](./world-rooms-presence-multiplayer.md) 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](./tara-instructors-classes-coaching.md);
the money mechanics it shares with concerts live in
[commerce, rights & takedown](./commerce-rights-takedown.md); the deep
engine/pipeline treatment is the architecture companion,
[../architecture/tara-classes-aja-and-commons.md](../architecture/tara-classes-aja-and-commons.md).
For the full feature scope this slots into, start at the hub:
[../V3_features.md](../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](../architecture/authoring-and-content-pipeline.md)).

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](./tara-instructors-classes-coaching.md); 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.

```mermaid
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.

```mermaid
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](./commerce-rights-takedown.md).

**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

- [Tara instructors, classes & coaching](./tara-instructors-classes-coaching.md)
  — onboarding/verification, the live-class lifecycle, on-demand library, and
  the Aja body-coaching loop these authored sequences feed.
- [Commerce, rights & takedown](./commerce-rights-takedown.md) — the 20/80
  split, tips/donations, the Tara Lineage Fund, and the Aje royalty waterfall
  this voice royalty rides into.
- [../architecture/tara-classes-aja-and-commons.md](../architecture/tara-classes-aja-and-commons.md)
  — the architecture-side treatment of the same `@oshun/tenant-tara-studio`
  policies, the Aja pose pipeline, and the Lilith Commons venues.
- [../architecture/authoring-and-content-pipeline.md](../architecture/authoring-and-content-pipeline.md)
  — the web-Studio-vs-UE authoring split, the Prisma `payloadHash` integrity
  spine, and how a published manifest reaches a client.
- The feature hub: [../V3_features.md](../V3_features.md).
