# Tara Live Classes, Aja Coaching & Lilith Commons

If the avatar/audio stack is V3's _embodiment substrate_, the three subsystems
on this page are what people actually come to **do** inside Lilith: take a
contemplative live class from a verified instructor (**Tara**), get their real
body coached from a webcam without the footage ever leaving the device
(**Aja**), and free-roam a shared social world of libraries, observatories,
debate halls, and gardens (**Lilith Commons**). Where combat in V2 had to be
_provably deterministic_, these tenants have a different burden of proof: they
have to be _provably safe and consent-shaped_. A yoga instructor's avatar must
not close its eyes or touch a student without explicit permission; an AI persona
must not teach an asana its human sponsor never approved; a student's pose data
must not leave their laptop; a stranger in the commons must be reportable with
60 seconds of replay evidence that stays inside the tenant's data-residency
region. Every one of those is encoded as a state machine or a fail-loud gate in
real TypeScript, not left as a comment.

These tenants live as a tier of domain libraries under `libs/v3/` —
`@oshun/aja-pose` (`libs/v3/aja-pose`), `@oshun/tenant-tara-studio`
(`libs/v3/tara-studio`), and `@oshun/tenant-lilith-commons`
(`libs/v3/lilith-commons`) — that own the pose math, the asana catalog, the
consent/governance policies, and the venue/programming/safety/localization
model. They sit on top of the embodiment runtime documented in the sibling page
and feed cue events and room state through the world server and gateway. This
page is the architecture-side companion for the "Avatars, Audio, and Tenant
Experiences" set; the section hub is
[../V3_ARCHITECTURE.md](../V3_ARCHITECTURE.md).

## What ships, honestly

The three libraries are **substantive, domain-specific TypeScript with real
algorithms and broad test coverage**, not CRUD shells. `aja-pose` ships a full
on-device pose pipeline (MediaPipe Tasks-Vision + MoveNet fallback), an
18-feature biomechanical extractor, a nearest-centroid asana classifier, six
known-risk modification ladders, a recursive raw-pose egress blocker, and a
Psyche-TTS cue-delivery budget model — 21 automation specs. `tara-studio` ships
a 300-entry asana library, guided practice-plan generation, and a stack of
consent/governance policies (eyes-open, physical-adjustment, language linter,
lineage grounding, AI-persona sponsorship/region-cap/script restriction, TTS
voice contracts) across ~16 spec suites plus a Prisma layer. `lilith-commons` is
the largest single library in V3 — a 13,458-line `index.ts` with a 4,536-line,
143-case test suite — covering eight launch venues, programming calendars and
festivals, a seven-surface accessibility suite, an 18-locale localization
pipeline, and an in-world safety model.

Five honest qualifications. **First**, Aja's "≥ 92 % per-asana accuracy" and "≥
90 % human-reviewed cue relevance" gates are computed against a **deterministic
synthetic corpus**, not real labeled webcam footage. The feature extractor
(`extractAsanaFeatureVector`) and the centroid classifier are genuine and run on
real MediaPipe landmarks at runtime, but `buildCanonicalAsanaTrainingCorpus`
synthesizes its samples from sinusoidal prototypes (`canonicalAsanaPrototype`)
plus small perturbations, and `buildAjaCueHumanReviewSet` builds its
"tara-senior-teachers" review cases programmatically from centroids. Treat those
numbers as _self-consistency proofs of the algorithm_, not field-measured
accuracy. **Second**, the pose models and the Psyche-TTS voice are
**runtime/provider-gated**: MediaPipe and MoveNet load via dynamic import (the
MediaPipe wasm bundle is vendored in `node_modules`, MoveNet/TF is not), and the
TTS path runs through an injected `AjaPsycheTtsAdapter` with a deterministic
test double; the 250 ms latency gate validates the _model_, not a live network.
**Third**, Tara's "300 asanas" is **30 base poses × 10 lineage framings**
generated combinatorially, each entry carrying real per-family contraindications
and modifications — substantial content, but not 300 distinct shapes.
**Fourth**, Sophia "grounding" in the lineage editor is a deterministic check
over a source registry, not a live LLM call. **Fifth**, the in-world _rendering_
of all this — world-space UMG widgets, Sequencer ritual rooms, the planetarium
dome — is UE content referenced by string (`/Game/Venues/Commons/…`); these
libraries own the data, policy, and orchestration tier. The sections below say
where each claim is backed.

## Aja: body-aware coaching that never sees your camera

Aja is the only V3 loop that grades a **real human body**. A student opts into
camera-based pose estimation; frames are processed entirely on-device, and only
derived features and aggregate metrics ever leave the client. The pipeline in
`libs/v3/aja-pose/src` is six honest stages.

```mermaid
flowchart LR
    cam["Webcam frame<br/>(opt-in)"] --> rt["Pose runtime<br/>MediaPipe → MoveNet"]
    rt --> feat["18-feature vector<br/>extractAsanaFeatureVector"]
    feat --> cls["Nearest-centroid<br/>asana classifier"]
    target["Active asana target<br/>(class sequence)"] --> cue
    cls --> cue["Alignment cue gen<br/>(10 rules + ML assist)"]
    cls --> risk["Risk flagger<br/>→ modification ladder"]
    cue --> voice["Psyche-TTS voice cue<br/>(≤ 250 ms, via V3Voice)"]
    cue --> text["Text cue overlay"]
    rt -. aggregate only .-> iris["Iris memory<br/>(raw egress blocked)"]
```

**The runtime and its fallback** (`runtime.ts`). The primary estimator is the
MediaPipe Tasks-Vision pose-landmarker (33 named landmarks), with a TensorFlow
MoveNet lightning/thunder detector (17 keypoints) as fallback. `AjaPosePipeline`
(`:399`) is a real fallback cascade: it initializes adapters in order, and on
any `init-failed`/`estimate-failed` advances to the next adapter and fires
`onFallback` — so a GPU-delegate failure degrades to CPU MoveNet rather than
dropping the student. Both device profiles (`web-m1-mac`,
`mobile-iphone-15-pro`) target **15 Hz** (`AJA_POSE_MINIMUM_HZ`, 66.7 ms
budget), and `assertAjaPosePerformanceGate` throws if observed Hz or inference
blows it; the estimators load through a `dynamicImport` seam so the geometry
unit-tests without wasm.

**Features and classification** (`classifier.ts`). `extractAsanaFeatureVector`
(`:391`) turns landmarks into 18 rotation/scale-robust biomechanical features
(`ASANA_FEATURE_NAMES`): shoulder/hip slope, spine angle, four limb joint angles
computed as `acos(dot / |a||b|)`, wrist/ankle spans, shoulder–hip twist, and
side lengths. `trainAsanaCentroidClassifier` (`:239`) averages per-asana vectors
into centroids over the 30 canonical asanas (`AJA_CANONICAL_30_ASANAS`, Tadasana
through Savasana); `classifyAsanaFeatureVector` returns the nearest centroid by
Euclidean distance with a first-to-second-distance confidence; and
`evaluateAsanaClassifier` builds a confusion matrix and enforces the ≥ 92 %
per-asana gate. As flagged above, the corpus is synthetic; the _classifier_ is
real.

**Alignment cues** (`cue-generator.ts`). Ten rules (`AJA_ALIGNMENT_CUE_RULES`,
`: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. Candidates are
sorted by `priority + deviation`, sliced to the top _N_, and, when a classifier
model is supplied, decorated with `AjaCueMlAssistance` (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 a single
`return-to-target` cue. The relevance of these cues is gated at ≥ 90 %
(`AJA_CUE_RELEVANCE_MIN_HUMAN_APPROVAL`) against the (synthetic) review set.

**The risk ladder** (`risk-flagger.ts`). Six known-risk rules
(`AJA_KNOWN_RISK_MODIFICATION_RULES`, `:102`) cover shoulderstand, plow, lotus,
chaturanga, upward-dog, and tree, each keyed to anatomical risk factors (`neck`,
`lumbar`, `knee`, `wrist`, `shoulder`, `balance`, `hypertension`, `pregnancy`)
and carrying a **three-step modification ladder** with real 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 only trips rules matching their declared risk
factors. `routeAjaRiskModification` routes within a 250 ms budget
(`AJA_RISK_MODIFICATION_BUDGET_MS`) and the gate requires _every_ validation
case to route correctly and on time.

**The privacy gate** (`privacy.ts`). This is the load-bearing safety property.
`containsAjaRawPoseData` (`: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` produces an Iris-bound envelope of
counts/averages only and runs it through `assertAjaNoRawPoseDataLeavesDevice`,
which **throws** if raw data is present. So the only thing that can reach Iris
is aggregate metrics, and the gate fails loud, not silent.

**Voice delivery** (`voice-cue.ts`). Cues are spoken in the instructor's own
voice through `deliverAjaVoiceCue`, which builds an `AjaPsycheTtsCueRequest`
(Opus, low-latency, routed `psyche-tts → v3voice` on a `student-private-cue`
channel) and measures pose-to-first-audio against the 250 ms budget. The tone
profile is validated (warmth/energy ∈ [0,1], speech rate ∈ [0.75,1.25]). The
provider is an injected adapter; the gate proves the budget, not the wire.

## Tara: the live-class pipeline, governed end to end

Tara instructors author **entirely from the web** — the V3_ARCHITECTURE
authoring split routes asana sequences, schedules, profiles, and recording
opt-in through the V1 Lilith Studio, and the UE5 client renders the published
manifest at runtime. `tara-studio` is the contract/policy tier behind that,
persisted through its own Prisma models (`V3LiveClassSession`,
`V3AsanaSequence`, `V3Asana`, `V3InstructorProfile`, `V3InstructorCredential`,
`V3AjaCueEvent`, `V3PracticePlan`, `V3OnDemandClassRecording`).

**The asana library** (`asana-library.ts`). `TARA_CANONICAL_ASANA_LIBRARY`
(`:234`) is 30 seeds × 10 lineage variants = 300 entries
(`TARA_ASANA_LIBRARY_GA_MINIMUM`). Each carries lineage teaching notes
(Iyengar-supported, Ashtanga vinyasa, trauma-informed, chair-accessible, …), ≥ 4
modifications, **family-keyed contraindications** (an inversion warns on neck
injury and hypertension; a backbend warns on lumbar compression and pregnancy),
an Aja cue bundle, an editorial signature, and a design review.
`validateTaraCanonicalAsanaLibrary` (`:240`) is an all-or-nothing GA gate: every
entry must be signed, design-approved, and complete.

**Onboarding to a plan** (`practice-plan.ts`). A 5-minute intake
(`TARA_GUIDED_ONBOARDING_DURATION_SECONDS`) maps a goal/experience/intensity to
a starter template and emits a 4-week schedule
(`TARA_PRACTICE_PLAN_DURATION_WEEKS`) of on-demand-class and self-practice
sessions; `practice-plan-adjustment.ts` re-tunes it from attendance, Aja
signals, and Arete logs.

**The consent and safety stack** is the heart of the tenant — five independent
gates, each a state machine with 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 explicit permissive pattern (`EXPLICIT_EYES_CLOSED_INVITATION_PATTERNS`: "if it feels", "you may", "optional", …) |
| No-surprise contact   | `physical-adjustment-consent.ts`  | Default `hands-off`; in-world contact is `blocked` until the student grants an explicit, dialog-matched consent record — otherwise `dialog-required`/`blocked`                                                      |
| Invitational language | `invitational-language-linter.ts` | 13 directive phrases (`must`, `should`, `force`, `push`, `hold`, …) are flagged **blocking**, forcing `needs-editorial-revision` before a sequence script ships                                                     |
| Class style mode      | `class-style-mode.ts`             | Directive cueing is only allowed in an explicitly-declared `advanced-directive-opt-in` class; the listing surfaces the declaration                                                                                  |
| Lineage grounding     | `lineage-grounding.ts`            | A lineage claim needs a citation; `groundLineageClaim` marks an uncited claim `needs-citation` and blocks editorial review                                                                                          |

The eyes-open policy is notable because it exists **twice and must agree**: this
TS Studio-side policy (`evaluateTaraSequenceEyeState`, `:43`) and the C++
runtime-side `V3TaraEyesOpenPolicy` documented in
[./avatar-animation-and-audio.md](./avatar-animation-and-audio.md) share the
same default and the same "explicit invitation required" rule, so authoring and
the avatar runtime can't disagree about whether an instructor's eyes may close.
The lineage grounder is a genuine little reasoning engine:
`buildTaraLineageEditorialReview` (`:204`) walks each claim against a source
registry with authority scores, and the fixture's uncited "direct Mysore family
line from 1932" claim (`TARA_LINEAGE_UNSOURCED_CLAIM_ID`) is exactly what blocks
the review.

**AI-persona governance.** Tara permits AI instructor personas, fenced by four
real gates. `registerTaraAiPersonaSponsor` (`ai-persona-sponsor.ts:107`)
requires a **verified human instructor** with credentials to sponsor a persona
and bind it to a closed scope set (`sequence-script-only`, `student-q-and-a`,
`booking-prep`, `safety-handoff`, …); a sponsor can disable it with an audit
trail. `evaluateTaraAiPersonaRegionalSchedulingCap`
(`ai-persona-region-cap.ts:50`) enforces **one AI persona per four verified
human instructors** per region by floor division — a region with three humans
gets zero personas. The sequence-script restriction routes any out-of-script
asana to `handoff-to-sponsor` or a `graceful-noop`, and the nameplate lock keeps
a non-removable "sponsored by a human" label on the persona. TTS voice
signatures get their own five-file sub-stack: opt-in consent with 30-day
withdrawal, a contract template that forbids cross-instructor/cross-tenant reuse
and grants a 5 % added royalty share, a recording-session pipeline, a runtime
scope-lock that auto-withdraws on misuse and alerts an operator, and a
royalty-distribution settlement.

A class itself runs the V3_ARCHITECTURE pipeline: schedule (web Studio) →
booking (V1 BFF) → room spawn (lilith-world-server) → UE clients load
`V3Mode_TaraLiveClass` while fallback clients load the three.js scene → students
opt into the camera feeding the **Aja loop above** → per-student cues render as
world-space widgets (UE) or HUD text (fallback) → the session logs to Arete and,
with opt-in, records to the on-demand library.

## Lilith Commons: the shared social space

Commons is the free-roam world — eight launch venues that re-render V1 domain
experiences inside UE5 with Tier-2 fallbacks. `lilith-commons` is where the bulk
of the safety, accessibility, and localization burden actually lives.

**Venues and capacity.** `V3_LILITH_COMMONS_LAUNCH_VENUE_IDS` (`:150`)
enumerates the eight — atrium, garden-of-cycles, the-stacks (Nisaba),
observatory (Nyx), debate-hall (Veritas), lecture-hall (Metis),
atrium-of-practice (Arete/Tara ritual rooms), and lantern-hall — across eight
venue _kinds_ and three capacity tiers (`quiet`=16, `standard`=64,
`assembly`=256). Each carries an editorial signoff and a design review, and the
launch-catalog report gates on all eight being live, signed, and approved.
Per-venue pipelines are modeled in detail: Veritas runs six radial podiums with
120-second turns and six Sophia inline citations rendered under 120 ms; Metis
seats 256 across eight tiers with eight breakout cells; Nisaba's open stack
exposes 216 editions across 12 shelves; and every venue gets one **voice-free,
avatar-hidden solitary cell**
(`/Game/Venues/Commons/SolitaryCell/LVL_CommonsSolitaryCell_Template`) so the
commons always has a quiet exit.

**Programming.** A daily ambient-loop calendar plus scheduled programming covers
every venue, with cross-tenant festivals as first-class report types: the
Equinox Festival (4–7 days), Lineage Week, Crossover Weekend, and a Remembrance
Hall memorial program with tone review and a two-reviewer moderation floor.

**Accessibility** is a seven-surface suite, each with a measured QA gate that
fails loud on regression: avatar-anchored live captions (web/mobile/VR, anchored
`avatar-head-top`, p95 ≤ 1000 ms); reduced-motion (camera ≤ 6°, avatar ≤ 1 cm,
teleport locomotion); three color-vision palettes (asana-cue contrast ≥ 4.5);
one-handed mobile HUD (controls in the top ≥ 56 %, hit targets ≥ 44 px);
photosensitive-safe mode (strobe ≤ 3 Hz, attenuation ≥ 90 %); cognitive-load
reduction (≤ 5 primary controls, dwell ≥ 1200 ms); and full
keyboard/single-switch navigation (scan ≤ 1200 ms).

**Localization** ships **real** translations, not metadata.
`launch-locale-hud-translations.ts` is the source of truth for six HUD strings
across the launch locales, and the suite asserts no English string is copied
through to a non-English locale (English regional variants legitimately share
wording). `buildV3LaunchLocalizationReport` (`:6680`) gates 18 enumerated
locales with ≥ 16 carrying HUD + dubbing + RTL coverage, atop a voice-dubbing
pipeline and a documented cultural-adaptation review.

**Safety** is the in-world reporting → review → sanction chain:

```mermaid
flowchart LR
    point["Point at avatar<br/>createV3InWorldReportDraft"] --> cat["Pick category<br/>(harassment … minor-safety)"]
    cat --> replay["Attach 60 s replay<br/>(voice + motion, tenant-resident)"]
    replay --> submit["submitV3InWorldReport"]
    submit --> sla["Operator review SLA<br/>(7-day, ≥ 0.95)"]
    sla --> sanction["Progressive sanction<br/>warning → mute → realm-ban → account-ban"]
    sanction --> appeal["Appeals routing"]
```

A reporter points at an avatar; the report attaches a **60-second replay
buffer** (`V3_IN_WORLD_REPORT_REPLAY_WINDOW_MS = 60_000`) of voice and
avatar-motion frames that stays in the tenant's residency region. The operator
SLA dashboard requires ≥ 0.95 compliance over seven consecutive days, and
`advanceV3ProgressiveSanctionState` (`:12470`) walks the audit-logged ladder
`warning → mute → realm-ban → account-ban`. Minor protection is a hard default:
`evaluateV3MinorProtectionDefaults` (`:12575`) locks four surfaces for a
13-year-old (`voice-chat`, `presence-non-cohort`, `signed-edition-purchase`,
`remix-licensing`) with no bypass, fires operator alerts within 30 s on flagged
interactions, and blocks an adult instructor from scheduling a minor cohort
class without a cleared background check. All of this is persisted through the
`V3Report3D`, `V3SpatialTranscript`, `V3EmbodiedConsent`, `V3Room`, and
`V3Venue` Prisma models.

## isis-motion: a labeled placeholder

`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 home of
the motion-authoring service that would sit between Tara's asana sequences /
Saraswati's motion presets and the retarget runtime. Reading it expecting a
motion engine will mislead; the real motion logic lives in `V3Avatar` (IK,
retarget, posture) and in the asana catalogs here.

## Edge cases and failure modes

- **Raw pose can't leak.** `assertAjaNoRawPoseDataLeavesDevice` throws on any
  egress payload containing a raw-pose key; Iris only ever receives counts and
  averages.
- **The pose runtime degrades, never dies.** A failed MediaPipe init or estimate
  cascades to MoveNet and fires `onFallback`; only when _all_ adapters fail does
  a frame throw.
- **Eyes-closed needs words, in two places.** Both the TS Studio policy and the
  C++ avatar policy suppress an auto eye-close cue absent an explicitly
  permissive invitation, and record the suppression reason.
- **Contact is blocked by default.** A physical adjustment is `dialog-required`
  until a student grants a consent record whose `dialogId` and `studentUserId`
  match — a stale or mismatched record stays `blocked`.
- **Directive language fails the build.** A single `must`/`force`/`push` in a
  sequence script flips the linter to `needs-editorial-revision`.
- **AI personas are rate-limited by humans.** Region cap is floor division:
  fewer than four verified humans means zero personas, full stop.
- **Reports keep their evidence local.** The 60-second replay is tagged with the
  tenant residency region and persisted only as attached-report data, never as a
  durable rolling transcript.
- **Synthetic-gate caveat.** Aja's accuracy/relevance gates prove the algorithm
  against generated data; they are not field accuracy.

## How it connects

These tenants consume V3's embodiment substrate. The instructor and student
avatars, the eyes-open/posture policies, and blendshape-driven expression all
come from [avatar, animation & spatial audio](./avatar-animation-and-audio.md);
Aja and `psyche-3d` are the two embodiment loops that meet in a Tara class — Aja
grades the human, psyche-3d animates the avatar. The premium-persona / concert
side of the same substrate is the
[Saraswati stage pipeline](./saraswati-stage-pipeline.md), which shares Tara's
TTS-voice and provenance machinery. Booking, room spawn, cue-event fan-out, and
presence ride the [world server and gateway](./world-server-and-gateway.md), and
the Tier-2 rendering of cues and venues is the
[fallback web client](./tier2-fallback-web-client.md). Where these `@oshun/*`
packages sit in the domain map — and how Iris, Arete, Nisaba, Nyx, Veritas, and
Metis reach into V1 — is catalogued in
[Oshun Domain Libraries](../../platform/oshun-domain-libraries.html); the module
topology is mapped in
[the subsystem glossary and layout](./subsystem-glossary-and-layout.md).

## Related

- [Avatar, Animation & Spatial Audio](./avatar-animation-and-audio.md) — the
  embodiment substrate (avatars, IK, posture, lip-sync, voice/music) these
  tenants render
- [Saraswati Stage Pipeline](./saraswati-stage-pipeline.md) — the premium
  MetaHuman performer / concert tenant that shares this voice + provenance stack
- [World Server and Gateway](./world-server-and-gateway.md) — room spawn,
  presence, and cue-event transport
- [Tier-2 Fallback Web Client](./tier2-fallback-web-client.md) — how cues and
  venues render without UE5
- [Oshun Domain Libraries](../../platform/oshun-domain-libraries.html),
  [Subsystem Glossary and Layout](./subsystem-glossary-and-layout.md) — package
  map and module topology
- The section hub: [../V3_ARCHITECTURE.md](../V3_ARCHITECTURE.md)
