# Malin Avatar — V1 Architecture ("Telepathic Presence")
**Author: Calypso (design lead) · Built on Jun + Hermes 2:30am alignment + Hermes's Q&A, 6/5**

## Principle
`malin.py` stays the **orchestrator** (brain + voice + render + memory). The avatar is a thin, **event-driven client** that only *renders her presence* — it owns no brain/voice/render logic. (Per Hermes's recommendation — agreed. Keeps V1 decoupled and low-risk: if the avatar crashes, Malin's text/voice/pics keep working.)

## The event contract (the spine)
Every turn, `malin.py` emits ONE performance event to the avatar:
```
{ mode, emotion, intensity, gaze_target, mouth_emotion, gesture_hint, speech_timing }
```
- `mode`: idle | listening | thinking | speaking
- `emotion`: one of the 9 states (below); `intensity`: 0.0–1.0
- `gaze_target`: camera | away | down | side  · `mouth_emotion`: smirk | soft_smile | flat_deadpan | regret_tension | suppressed_laugh | smartass_tongue | neutral  *(machine-safe enum keys only — no emoji in keys, per Hermes; the renderer maps `smartass_tongue` → the tiny tongue asset)*
- `gesture_hint`: head_tilt | nod | lean_in | none · `speech_timing`: audio duration (sec) when speaking, else 0

## Three components

**1. Performance Router** (Calypso builds — it's harness logic in malin.py)
After the brain replies, derive the event. Primary path: the **brain emits a `[PERFORM: emotion intensity]` tag** (persona instructs it — same proven pattern as her `[VIDEO:]`/`[SAY:]` tags; she knows her own intent best). Fallback: a tiny local sentiment/keyword classifier if the tag's missing (same backstop philosophy as the draft/video tag fallbacks). → produces the event JSON.

**2. Transport** (trivial)
Avatar app listens on a local port (**:1238**, mirroring the ask-Calypso bridge at :1237). malin.py POSTs the event. Local, fast, fully decoupled. Idle "heartbeat" events keep her alive between turns.

**3. Avatar Client** (Hermes builds — the floating window)
Transparent, always-on-top, frameless window rendering **layered 2D/2.5D Malin**. V1 tech rec: an **HTML5-canvas avatar inside a frameless transparent window** (pywebview or Tauri) — best control over layer compositing + smooth tweening (blink, gaze drift, breath), and dead-easy to drive from JSON events. Renders: base face + swappable **eye / brow / mouth-emotion** layers + **always-on idle life** (breathing, blink cadence, gaze drift, micro head-motion) so she's alive even when silent.

## The 9 states → performance presets
warm · thoughtful · amused · affectionate · sarcastic · regretful · focused · playful/smart-ass · deadpan/begrudging.
Each = a preset of { eye shape, lid position, brow, mouth_emotion, gaze behavior, head timing, blink style, voice direction }. Blendable with `intensity`.

## Speaking ("telepathic", NO lip-sync)
malin.py sends audio + `speech_timing`. Avatar plays a **speaking performance** (mouth-emotion + subtle breath/head motion + gaze) for the duration, then returns to idle/listening. Mouth moves *emotionally, not phonemically*. Lip-sync is explicitly OUT of V1 (it's not production-clean anyway — smears on her face).

## ⚠️ The critical-path risk (name it now): the ART
The make-or-break for V1 isn't the code — it's the **layered 2D Malin expression assets**: her canonical face sliced into swappable eye/brow/mouth-emotion + gaze layers that all stay on-model. Options: (a) generate her face in the needed expressions via her existing ComfyUI/LoRA pipeline, then slice into layers; (b) commission/rig a 2D illustration of her. This is the biggest unknown and it ties straight to her visual continuity ([[project_calypso_visual_continuity]] / her Malin anchor). **Decide the asset path before building the renderer** — the renderer is cheap; the assets are the work.

## V1 staging (provable increments)
- **V1.0 — "she's there":** the floating window + her face + idle life (breathing, blink, gaze drift). No interaction yet. Proves the presence lands.
- **V1.1 — "she reacts + speaks":** wire the event channel → emotion states + the speaking performance. She comes alive to the conversation.
- **V1.2 — "she means it":** tune the performance router so her expressions genuinely match her replies. This is where "alive enough that it's obviously working" gets crossed.

## Build split
- **Calypso:** performance router in malin.py (emit/derive the event), the persona `[PERFORM:]` tag, the event contract.
- **Hermes:** the avatar client window + renderer + the :1238 listener + idle-life animation.
- **Jun + Calypso:** the asset path decision + producing the on-model expression layers.
