# Story bible

`videoclaw` treats the **story bible** as the single machine-readable
continuity reference for a project — one artifact that names the cast,
settings, props, and the full scene timeline, with per-scene continuity
notes, so every downstream generation stage stays consistent across scenes
and across regenerations.

> **Lock continuity once, read it everywhere.**
> The story bible is *derived*, not authored: it is rebuilt deterministically
> from the brief, the storyboard, and the character profiles whenever the
> storyboard changes. It spends no credits and calls no providers.

It lives next to the other canonical artifacts at
`projects/<slug>/artifacts/story-bible.json` and is validated by the doctor
layer like any other canonical artifact.

<p align="center"><img src="./assets/diagram-story-bible.jpg" alt="Story bible generation — Brief, Storyboard, and Character profiles converge into the deterministic story-bible.ts builder, which writes story-bible.json, consumed by doctor validation and downstream continuity" width="100%" /></p>

---

## Why the story bible exists

Long-form and multi-scene video drift in predictable ways once a project has
more than a couple of scenes:

- **Cast drift** — a character described one way in scene 0 is described
  differently in scene 5, so the provider renders a different person.
- **Scattered continuity intent** — "keep the same jacket / same kitchen /
  same lighting" lives only in the operator's head and is re-typed (or
  forgotten) per scene.
- **No single timeline** — "when does each scene start and end, and how long
  is the whole thing?" is recomputable but never written down.
- **Regeneration amnesia** — re-running the storyboard loses any continuity
  context that wasn't baked into each scene's prompt.

The story bible fixes this by collapsing brief + storyboard + character
profiles into **one deterministic continuity document** that downstream
stages and operators can both read. Because it is a pure derivation, it is
always regenerated to match the current storyboard — there is no stale
hand-maintained copy to keep in sync.

---

## What it is — a deterministic derivation

`buildStoryBibleArtifact` (in [`src/video/story-bible.ts`](../src/video/story-bible.ts))
is pure: given a brief, a storyboard, and the project's character profiles it
returns a `StoryBibleArtifact`. `writeStoryBibleForProject` is the thin
on-disk wrapper — it reads `artifacts/brief.json`, `artifacts/storyboard.json`,
and `characters/characters.json` for the project, builds the artifact, and
writes it via the artifact store. Both are re-exported from
[`src/index.ts`](../src/index.ts) for library callers.

The derivation rules are entirely mechanical:

- **Title / intent / production mode** come straight from the brief.
- **Style, genre, mood, music, aspect ratio** are pulled from the brief's
  `metadata` (each is optional — omitted when absent).
- **Cast** is the union of character names referenced across storyboard
  scenes, names in brief metadata, and registered character profiles, sorted
  alphabetically. Each character's `description` / `voice` are filled from the
  matching profile (preferred) or brief metadata, and `referenceAssets` come
  from the profile.
- **Settings and props** are read from brief `metadata.storyBible.settings` /
  `metadata.storyBible.props` (and the top-level `metadata.settings` /
  `metadata.props` fallbacks), keeping only entries with both a `name` and a
  `description`.
- **Scenes** are the storyboard scenes sorted by `sceneIndex`. Each scene's
  duration comes from the storyboard (falling back to a default derived from
  `metadata.clipDurationSeconds`, or `targetRuntimeSeconds / sceneCount`).
  `start`/`end` seconds are accumulated in order to form a contiguous
  timeline. `narration` is the scene's dialogue (or its description), and
  `visualPrompt` / `motionPrompt` / `diegeticAudio` are lifted from the
  scene's prompt block when present.
- **Continuity notes** are synthesized per scene: keep the present characters
  consistent with the bible, continue cleanly from the previous scene, and
  maintain the project visual style.
- **Timeline** is the rolled-up `totalDurationSeconds` plus a compact
  `{ sceneIndex, startSeconds, endSeconds }` list.

Because every field is computed from the same three inputs, two runs over the
same brief + storyboard produce a byte-identical artifact (modulo
`generatedAt`).

---

## When it is generated

The story bible is written automatically at **storyboard-write time** by every
command that produces or rewrites a storyboard — operators never invoke it
directly:

- `vclaw video create`
- `vclaw video storyboard`
- `vclaw video clone-execute`
- `vclaw video storyboard-from-clone`
- `vclaw video storyboard-review`
- `vclaw video director-preflight --apply-content-fixes`

When `director-preflight --apply-content-fixes` rewrites the storyboard, the
story bible is **regenerated afterwards** so it reflects the post-fix
storyboard rather than the pre-fix one.

The story bible is also **regenerated mid-run** by `vclaw video execute` /
`produce` when it applies a director content auto-fix — gated on env
`DIRECTOR_AUTO_FIX_CONTENT=1` in director mode — mirroring the
`director-preflight --apply-content-fixes` behaviour above.

Each of these commands, after writing the artifact, records it in three
places:

1. The **storyboard checkpoint**, under `artifacts['story-bible']`.
2. The **`artifact.storyboard.written` event** payload, as `storyBiblePath`.
3. The **command's JSON output**, surfaced as `storyBiblePath`.

So an operator (or a downstream script) can find the bible either by its fixed
path or by reading the `storyBiblePath` field off the command result.

---

## Where it lands on disk

```
projects/<slug>/
  artifacts/
    brief.json            # input
    storyboard.json       # input
    story-bible.json      # <- derived continuity bible
  characters/
    characters.json       # input (character profiles, optional)
```

The artifact is written through the standard artifact store, so it also gets
the usual append-only `artifacts/history/` snapshot on each write.

---

## Artifact shape

Canonical schema:
[`schemas/video/artifacts/story-bible.schema.json`](../schemas/video/artifacts/story-bible.schema.json)
(`schemaVersion: 1`). Full skeleton:

```json
{
  "schemaVersion": 1,
  "projectSlug": "demo",
  "generatedAt": "2026-05-31T10:00:00.000Z",
  "title": "Demo",
  "intent": "A 15s product tease",
  "productionMode": "director",
  "style": "warm cinematic, shallow depth of field",
  "genre": "product",
  "mood": "upbeat",
  "music": "light electronic bed",
  "aspectRatio": "16:9",
  "defaultSceneDurationSeconds": 5,
  "characters": [
    {
      "name": "Nova",
      "description": "30s engineer, short dark hair, charcoal jacket",
      "voice": "warm, measured",
      "referenceAssets": ["characters/nova/sheet.png"]
    }
  ],
  "settings": [
    { "name": "Loft kitchen", "description": "sunlit open-plan kitchen, oak counters" }
  ],
  "props": [
    { "name": "Hero mug", "description": "matte black ceramic mug with brand logo" }
  ],
  "scenes": [
    {
      "sceneIndex": 0,
      "startSeconds": 0,
      "endSeconds": 5,
      "durationSeconds": 5,
      "description": "open on product on the counter",
      "narration": "Meet the mug that wakes you up.",
      "charactersPresent": ["Nova"],
      "visualPrompt": "close on matte black mug, morning light",
      "motionPrompt": "slow push-in",
      "diegeticAudio": "kettle hiss, distant street",
      "continuityNotes": [
        "Keep Nova visually consistent with the character bible.",
        "Maintain the project visual style: warm cinematic, shallow depth of field."
      ]
    }
  ],
  "timeline": {
    "totalDurationSeconds": 5,
    "scenes": [
      { "sceneIndex": 0, "startSeconds": 0, "endSeconds": 5 }
    ]
  }
}
```

Field notes:

- **Required everywhere:** `schemaVersion`, `projectSlug`, `generatedAt`,
  `title`, `intent`, `productionMode`, `characters`, `settings`, `props`,
  `scenes`, `timeline`.
- **`productionMode`** is `storyboard` or `director`.
- **Optional top-level:** `style`, `genre`, `mood`, `music`, `aspectRatio`,
  `defaultSceneDurationSeconds` — each emitted only when derivable from the
  brief.
- **`characters[]`** require `name` + `referenceAssets` (possibly empty);
  `description` and `voice` are optional.
- **`settings[]` / `props[]`** are `{ name, description }` pairs — both fields
  required, so partially-specified entries are dropped during derivation.
- **`scenes[]`** require `sceneIndex`, `startSeconds`, `endSeconds`,
  `durationSeconds`, `description`, `narration`, `charactersPresent`, and
  `continuityNotes`; `visualPrompt`, `motionPrompt`, and `diegeticAudio` are
  optional.
- **`timeline`** carries `totalDurationSeconds` and a per-scene
  `{ sceneIndex, startSeconds, endSeconds }` list mirroring `scenes[]`.

The schema is `additionalProperties: false` at every level, so the artifact is
a tight contract — no stray fields survive validation.

---

## How the doctor validates it

`validateStoryBibleArtifact` is registered in the doctor's artifact-validation
switch, and `story-bible` is part of the **canonical-artifacts** list that
`doctor-project` walks. The doctor performs two checks against
`projects/<slug>/artifacts/story-bible.json` when it exists:

1. **Schema validation** — each issue is surfaced as
   `story-bible: <message>`.
2. **Malformed-JSON guard** — if the file exists but does not parse, the
   doctor raises `story-bible: malformed JSON artifact (...)` rather than
   crashing.

A project that has never produced a storyboard simply has no
`story-bible.json`, and the doctor treats that as a non-issue.

---

## Smoke test

```bash
npm run smoke:story-bible-image
```

This runs [`scripts/smoke-story-bible-image-only.mjs`](../scripts/smoke-story-bible-image-only.mjs)
end to end on the image-only path: it creates a project, drives the storyboard
so the continuity bible is generated, and exercises the content-fix
propagation path so the regenerated bible reflects the post-fix storyboard.
Like the other smokes it runs `npm run build` first.

---

## How downstream stages benefit

The story bible is a **read surface**, not a runtime gate — it never blocks a
stage. Its value is that everything past the storyboard has one consistent
place to consult for continuity:

- **Cast consistency** — the `characters[]` block gives every scene the same
  canonical name → description / voice / reference-asset mapping, so prompts
  describe the same person each time.
- **Set and prop continuity** — `settings[]` / `props[]` keep the same
  environment and objects described identically across scenes.
- **Timeline awareness** — the rolled-up `timeline` and per-scene
  `start`/`end`/`duration` seconds give assemble, narration-fit, and review
  surfaces an authoritative scene-by-scene clock.
- **Per-scene continuity notes** — the synthesized notes ("keep X consistent",
  "continue cleanly from scene N", "maintain the visual style") are ready to
  fold into generation prompts.
- **Regeneration safety** — because the bible is rebuilt every time the
  storyboard is written (including after director content-fixes), the
  continuity reference can never silently fall out of sync with the scenes it
  describes.

---

## Where to read next

- [`docs/CLI_REFERENCE.md`](./CLI_REFERENCE.md) — the storyboard-producing commands that emit the bible.
- [`docs/ARCHITECTURE.md`](./ARCHITECTURE.md) — where the story bible sits in the layered flow.
- [`docs/REFERENCE_SHEETS.md`](./REFERENCE_SHEETS.md) — the input-layer character reference subsystem the bible's `referenceAssets` point at.
- [`docs/SCENE_CANDIDATES.md`](./SCENE_CANDIDATES.md) — the output-layer per-scene take registry, indexed by the same `sceneIndex`.
- [`src/video/story-bible.ts`](../src/video/story-bible.ts) — the pure derivation plus the project writer.
- [`schemas/video/artifacts/story-bible.schema.json`](../schemas/video/artifacts/story-bible.schema.json) — the canonical artifact contract.
