# Production library — cast & assets (`library.json`, schema `mvlibrary/v1`)

A reusable bank of **entities** — characters, props, vehicles, locations, wardrobe, accessories, buildings,
cities — with reference images per angle/pose and a canonical prompt fragment. Entities live outside any
single project so the same character/place stays consistent across shots and across projects.

## Discovery (nearest-wins — no auto-merge)

A `library.json` can sit at any folder. Resolving the library for a project uses the **single nearest**
`library.json`, walking up from the project's folder to the workspace root — the closest one wins outright.
Libraries are **not** merged: a folder that has its own `library.json` sees *only* that file, so each folder
explicitly owns which cast/assets its projects can use (you decide what to reuse, rather than inheriting
everything).

```
workspace/
  library.json              ← used by projects that have no closer library
  clients/acme/
    library.json            ← projects here use ONLY this (the root library is not merged in)
    spot.mvideo.json
```

To reuse a root entity inside a folder that has its own library, copy the entity into that folder's
`library.json` (e.g. re-run `entity_add` with its `libraryPath`). An invalid/broken nearer file is skipped
and discovery keeps climbing.

## Shape

```jsonc
{
  "$schema": "mvlibrary/v1",
  "entities": {
    "ana": {
      "id": "ana",
      "category": "character",              // character|prop|vehicle|location|wardrobe|accessory|building|city|other
      "name": "Ana",
      "description": "28, freckles, short dark hair, red parka",
      "promptFragment": "Ana, 28, freckles, short dark hair, red parka",  // injected into prompts
      "seedLock": 4412,                     // reuse for a stable look
      "tags": ["lead", "barista"],
      "views": {                            // angle/pose → workspace-relative image
        "front": "library/ana/front.png",
        "side": "library/ana/side.png",
        "3q": "library/ana/3q.png",
        "back": "library/ana/back.png"
      }
    },
    "the-van": { "id": "the-van", "category": "vehicle", "name": "Delivery van", "views": { "hero": "library/van/hero.png" } },
    "cafe": { "id": "cafe", "category": "location", "name": "Café Aurora interior", "views": { "wide": "library/cafe/wide.png" } }
  }
}
```

## Building an entity (character sheet)

1. `entity_add { id, category, name, description, promptFragment, seedLock }` — create the record.
2. Generate the first view: `image_generate` with the `promptFragment` + `seedLock` (e.g. "…, front view,
   neutral pose, plain background").
3. Generate more angles: `image_generate` again with the same `seedLock` **and the first view as
   `imagePaths`** (so the model matches it), varying the prompt ("side view", "3/4 view", "back view").
4. Record them: `entity_add { id, views: { front: "…", side: "…", "3q": "…" } }` (merges into the entity).

## Character voice (consistent speech)

A character entity can carry a `voice` so its speech is the same in every shot — the visual `views` keep the
face consistent; `voice` keeps the sound consistent:

```jsonc
"ana": {
  "id": "ana", "category": "character", "name": "Ana",
  "views": { "front": "library/ana/front.png" },
  "voice": {
    "provider": "elevenlabs",          // TTS provider
    "voiceId": "AnaClonedVoiceId",      // stable id → the SAME voice every time
    "model": "eleven_multilingual_v2",
    "settings": { "stability": 0.4, "similarity": 0.8 },
    "sample": "library/ana/voice.mp3",  // reference audio (voice cloning / matching)
    "description": "warm, mid-20s, slight Lisbon accent, unhurried",  // brief for models that take one
    "language": "en"
  }
}
```

Two ways it's used:
- **Voiceover / dialogue (TTS):** call `generate_speech` with the character's `voice.voiceId` (+ provider,
  model, settings) so every line in every shot uses the same voice. Put the result on a `role:"voice"`
  audio track.
- **Video-with-audio / lip-sync generation:** for models that speak (or a lip-sync step), pass the
  `voice.description` and/or the `voice.sample` as the voice brief, and the shot's `dialogue` as the line to
  say — so the generated audio matches the character.

Set it with `entity_add { id, voice: { … } }` (merges into the entity). Build a clone `sample` once from a
short recording or a `generate_speech` output, then reuse the entity everywhere.

## Using an entity in a shot

- Tag the clip with the entities it features: `update_clip { clipId, patch: { entities: ["ana", "cafe"] } }`.
- When generating that shot's video/still, pass the entity's matching `view` as `imagePaths`, include its
  `promptFragment` in the prompt, and pass its `seedLock` — so the look is consistent.
- Prefer reusing an entity's existing views over regenerating from scratch. For a wardrobe/lighting change,
  make a **variant entity** (`ana-evening`) rather than editing the base.

## In the editor

The editor's **Cast & Assets** panel lists the resolved library (thumbnails from each entity's first view)
and lets you attach an entity to the selected clip. Creating/editing entities and generating views is done
through the tools above.
