# Composition Surface and Cinematographic Technique Catalog

The Composition Surface is the customer- and creator-facing affordance that
arranges Segments into a Scene Score. The Cinematographic Technique Catalog is
the named layer that maps creator intent — "match cut," "L-cut," "smash cut" —
onto the Blend Kernel's mechanical transition primitives, gated per Segment
pair, per template, and per Lilith tone band. This page is part of the Living
Scenes core (see
[Living Scenes — Concept and Customer Promise](./living-scenes-overview.md) and
[Scene Score Schema](./scene-score-schema.md)); it sits one layer above the
Blend Kernel's transition contracts and one layer below the rendered video that
the Conductor streams. Everything documented here is **deterministic, pure
decision logic** — the catalog, the compatibility scorer, the tone gates, and
the compose-assist budgets are all real, contract-backed TypeScript with tests.
The pixel-level work they govern (latent video generation, optical-flow warps,
FVD computation, GPU determinism) lives downstream and is consumed only as
**score inputs**, never computed in these modules.

> **What is real vs. what is modeled.** The honest boundary matters here. The
> Blend Kernel (`@yemaya/blend-kernel`) "owns the contracts + parameter
> validation"; its own header states _"Implementations of the actual DSP /
> shader code live downstream."_ The compatibility scorer is a pure function
> over its inputs (`same pair → same scores`). FVD, flicker, color-jump and
> motion-vector numbers are **thresholds the pure functions compare against**,
> not measurements these modules take. When this page says a technique "requires
> a shape-similarity score ≥ 0.78" it is describing an _eval threshold the
> catalog declares_, not an embedding distance computed in this code — see the
> [Accuracy notes](#accuracy-notes-real-vs-aspirational) at the end.

## Where the Code Lives

| Concern                                         | Package / file                                                                                                                                                  |
| ----------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Technique catalog (12 entries), tone gating     | `@yemaya/blend-kernel` v0.1.0 — `libs/yemaya/blend-kernel/src/catalog/cinematographic-catalog.ts`, `tone-gating.ts`                                             |
| Transition primitives + parameter validation    | `libs/yemaya/blend-kernel/src/transitions.ts`                                                                                                                   |
| Segment-pair compatibility scorer               | `libs/yemaya/blend-kernel/src/compatibility/scorer.ts`                                                                                                          |
| Conductor runtime, compose-assist, cue parser   | `@yemaya/living-scenes-runtime` v0.1.0 — `libs/yemaya/living-scenes-runtime/src/{conductor/conductor.ts, compose-assist/compose-assist.ts, cues/cue-parser.ts}` |
| Score / Segment / RenderEnvelope Zod contracts  | `libs/contracts/src/living-scene/score.ts`                                                                                                                      |
| Template catalog + per-template eval gates      | `libs/isis/workflow-classes/src/living-scene/template-catalog.ts`                                                                                               |
| Tara sub-variant breath cycles                  | `libs/isis/workflow-classes/src/living-scene/tara.ts`                                                                                                           |
| Scene telemetry events (Live Direction channel) | `libs/oshun/embodiment-psyche/src/events/scene-events.ts`                                                                                                       |
| Customer compose surface (web / mobile)         | `apps/oshun/web/src/app/studio/compose/ComposeClient.tsx`, `apps/oshun/mobile/app/compose.tsx`                                                                  |
| AAA / operator Score editors                    | `apps/yemaya/studio-web/src/score-editor/ScoreEditorPage.tsx`, `apps/oshun/web/src/app/lilith-studio/scenes/TaraSceneEditor.tsx`                                |
| Public scene viewer + BFF                       | `apps/oshun/web/src/app/scene/[id]/{page.tsx,embed,report}`, `apps/oshun/bff/src/routes/living-scenes.ts`                                                       |

## Customer-Tier Composition Surface

V1 adds a curated composition affordance to the customer tier — distinct from
the Live Direction Channel (which steers a single template in real time) and
from the operator-tier Scene Score Editor (which exposes the full schema). The
customer surface lets users assemble a Score from existing Segments and preview
the resulting arc before committing.

- **Surfaces**: web at `apps/oshun/web/src/app/studio/compose/` (the
  `ComposeClient.tsx` client component) and mobile at
  `apps/oshun/mobile/app/compose.tsx`. Tablet gets the desktop affordance with
  touch-first interactions.
- **Affordances**: drag-arrange Segment cards on a timeline; tap a boundary to
  open the Technique Picker (showing only catalog entries compatible with the
  Segment pair _and_ allowed by the current template and tier, via
  `templateAllowsCompositionTechnique(...)`); scrub-preview the boundary at the
  picked technique's `overlapWindowRange`; one-tap accept an AI-suggested cut.
- **Sources of Segments** (modeled as `CompositionSegmentSource` with a `kind`
  discriminant in `compose-assist.ts`): (a) `kept-living-scene` Segments the
  user owns — re-composable across templates only when the destination allows it
  and the source is `userOwned`; (b) per-template segment libraries (Tara
  breath-cycle anchors, Nyx celestial anchors, Veritas topic-hub anchors, Metis
  lesson-step anchors, Arete intention-derived anchors); (c)
  `tenant-rights-import` Segments, gated on the `composition-import` rights tag.
  This logic is the real `canUseSegmentSource(...)` function.
- **Tier caps** (`CompositionTier = 'free' | 'paid' | 'aaa'`, enforced by the
  literal `TIER_CAPS` table in `compose-assist.ts:447-452`):

  | Tier   | Max Segments | Max duration  | Behavior                                                                |
  | ------ | ------------ | ------------- | ----------------------------------------------------------------------- |
  | `free` | 6            | 90 s          | curated compose surface                                                 |
  | `paid` | 24           | 8 min (480 s) | curated compose surface                                                 |
  | `aaa`  | — (no cap)   | —             | `routesToStudioEditor: true` — full Scene Score Editor in Yemaya Studio |

  Crisis frame disables composition entirely: when `crisisFrameActive` is true,
  `validateCompositionDraft` returns `allowed: false` with reason
  `crisis-frame-composition-lock` _before_ tier caps are even evaluated. Active
  sessions complete on the technique already picked; no new composition is
  permitted while the frame is active.

- **Score lifecycle**: composition produces a `CompositionDraft`; promotion to a
  renderable Score (`promoteCompositionDraft`) requires every boundary to clear
  the continuity gate (`continuityPreScore ≥ 0.85`, enforced both at the draft
  level and per boundary — `compose-assist.ts:585` and `:688` respectively),
  every Segment to pass `workflowClassPolicyAllowed`, and every Segment source
  to clear `canUseSegmentSource`. Promoted Scores route through the standard
  Conductor + Render Envelope path (see
  [Direction, Conductor, and Blend](#the-conductor-runtime-state-machine)).
- **Live Direction co-existence**: a customer-composed Score still accepts Live
  Direction cues at runtime under the verb vocabulary, with the composition-time
  technique respected unless a cue overrides it. The real
  `resolveRuntimeTechnique` function shows the precedence: a `kill` cue always
  wins (returns `'kill'`); a `linger` cue extends the current technique (returns
  `${techniqueId}:linger-extended`); otherwise the planned technique stands.
- **Reduced-motion variant**: every promoted Score ships a reduced-motion
  mapping automatically. `promoteCompositionDraft` builds
  `reducedMotionTechniquesByBoundary` by running every boundary technique
  through `reducedMotionTechniqueFor(...)`, which reads the per-technique
  `REDUCED_MOTION_TECHNIQUE_FALLBACKS` table (e.g. `whip-pan → fade-to-black`,
  `match-cut → dissolve`, `montage → still-frame`).

## Cinematographic Technique Catalog

The catalog is a versioned, frozen, contract-backed taxonomy. Each entry binds
creator intent to one or more Blend-Kernel primitives, declares its constraints,
and ships per-technique continuity-eval thresholds. V1 launches with **exactly
twelve techniques** (`TECHNIQUE_IDS`, `cinematographic-catalog.ts:16-29`), each
with a `provenanceTag` of the form `technique:<id>` recorded in the Render
Envelope; the catalog is extensible behind Isis release gates and pins versions
per kept artifact so deprecation never breaks playback.

### Technique Schema

The `CinematographicTechnique` interface (`cinematographic-catalog.ts:54-67`) is
richer than the prose summary:

```ts
interface CinematographicTechnique {
  id: TechniqueId; // one of the 12
  displayName: string;
  kernelComposition: readonly TransitionKind[]; // which Blend-Kernel primitives
  overlapWindowRange: { minSeconds; maxSeconds };
  requires: readonly string[]; // semantic preconditions
  forbids: readonly string[];
  toneBands: readonly ToneBand[]; // contemplative|pedagogical|narrative|cinematic|entertainment
  templateAllowlist: readonly string[]; // canonical template IDs
  evalThresholds: EvalThresholds; // maxFvd, maxFlicker, maxColorJump, maxMotionVectorDelta, minNarrationAlignment
  accessibilityNote: string;
  reducedMotionFallback:
    | 'hard-cut'
    | 'dissolve'
    | 'fade-to-black'
    | 'still-frame';
  provenanceTag: string; // matches /technique:[a-z0-9-]+/
}
```

`ToneBand` here (`contemplative`, `pedagogical`, `narrative`, `cinematic`,
`entertainment`) is the _catalog's_ tone vocabulary; it is distinct from the
Score-level `ToneClass` enum on a Segment (`contemplative`,
`gentle-instructive`, `celebratory`, `reverent`, `reflective`, `wonder`) defined
in `score.ts:28-35`.

### V1 Launch Catalog — Kernel Composition and Eval Thresholds

The table below is taken verbatim from `V1_TECHNIQUE_CATALOG`. The `maxFvd`,
`maxFlicker`, `maxColorJump`, `maxMVΔ` (motion-vector delta), and `minNarrAlign`
(narration alignment) columns are the _actual_ per-entry numbers — the values
the downstream continuity scorecard is checked against, not measurements taken
in this module.

| Technique       | Kernel composition (`TransitionKind`s)                                            | Overlap (s) | maxFvd | maxFlicker | maxColorJump | maxMVΔ | minNarrAlign | Reduced-motion fallback |
| --------------- | --------------------------------------------------------------------------------- | ----------- | ------ | ---------- | ------------ | ------ | ------------ | ----------------------- |
| `hard-cut`      | _(none — instant)_                                                                | 0–0         | 0.40   | 0.04       | 0.12         | 0.40   | 0.90         | `hard-cut`              |
| `match-cut`     | latent-warm-start, optical-flow-morph, motion-descriptor-handoff, color-lut-match | 0.5–2.0     | 0.30   | 0.04       | 0.10         | 0.30   | 0.92         | `dissolve`              |
| `l-cut`         | audio-crossfade, color-lut-match                                                  | 1.0–4.0     | 0.40   | 0.04       | 0.12         | 0.40   | 0.94         | `hard-cut`              |
| `j-cut`         | audio-crossfade, color-lut-match                                                  | 1.0–4.0     | 0.40   | 0.04       | 0.12         | 0.40   | 0.94         | `hard-cut`              |
| `dissolve`      | latent-warm-start, optical-flow-morph, color-lut-match, audio-crossfade           | 0.5–3.0     | 0.30   | 0.03       | 0.08         | 0.30   | 0.90         | `dissolve`              |
| `smash-cut`     | motion-descriptor-reset, audio-level-jump                                         | 0–0.2       | 0.50   | 0.06       | 0.18         | 0.60   | 0.85         | `hard-cut`              |
| `whip-pan`      | optical-flow-morph, motion-descriptor-handoff, variable-rate-sequencer            | 0.2–0.8     | 0.50   | 0.05       | 0.18         | 0.70   | 0.85         | `fade-to-black`         |
| `jump-cut`      | motion-descriptor-reset, variable-rate-sequencer                                  | 0–0         | 0.60   | 0.06       | 0.20         | 0.60   | 0.85         | `dissolve`              |
| `match-action`  | latent-warm-start, optical-flow-morph, motion-descriptor-handoff                  | 0.3–1.5     | 0.35   | 0.04       | 0.12         | 0.35   | 0.90         | `dissolve`              |
| `cross-cut`     | narrative-pivot, audio-crossfade                                                  | 0–0         | 0.40   | 0.05       | 0.15         | 0.45   | 0.88         | `hard-cut`              |
| `montage`       | variable-rate-sequencer, audio-crossfade, color-lut-match                         | 1.0–6.0     | 0.45   | 0.05       | 0.15         | 0.50   | 0.85         | `still-frame`           |
| `fade-to-black` | color-lut-match, audio-crossfade, motion-descriptor-reset                         | 0.5–2.0     | 0.30   | 0.03       | 0.10         | 0.30   | 0.92         | `fade-to-black`         |

Notable per-entry semantics drawn from `requires`/`forbids`:

- **`hard-cut`** and **`dissolve`** both carry
  `forbids: ['active-narration-mid-sentence']` — you may not hard-cut or
  dissolve through a spoken sentence.
- **`match-cut`** requires `shared-shape-anchor`; **`l-cut`/`j-cut`** require
  `audio-bus-continuity`; **`smash-cut`** requires `percussive-audio-spike` and
  `forbids: ['contemplative-tone-band']`; **`whip-pan`** requires
  `camera-pan-continuity` and likewise forbids the contemplative band;
  **`jump-cut`** forbids both `contemplative-tone-band` and
  `pedagogical-tone-band`; **`cross-cut`** requires `parallel-action`;
  **`montage`** requires a `shared-music-bed`.
- **`fade-to-black`** is the universal fallback: it has empty
  `requires`/`forbids`, is on `ALL_TEMPLATES`, and is the recovery transition
  the policy gate demands for crisis boundaries. It is the graceful default for
  end-of-arc, section breaks, and any cut that fails every other technique's
  compatibility check.

### Kernel Primitives the Catalog Composes

The `kernelComposition` column references the nine `TransitionKind`s defined in
`transitions.ts:13-23`. The doc historically listed only five; the real array is
**nine**, each with its own typed parameter object and validated range
(`validateTransition`, `transitions.ts:106-222`):

| `TransitionKind`            | Validated parameters and ranges                                              |
| --------------------------- | ---------------------------------------------------------------------------- |
| `latent-warm-start`         | `bridgeSteps ∈ [2, 12]` (integer); optional `cfgScale ∈ [0, 30]` or `null`   |
| `optical-flow-morph`        | `morphFrames ∈ [1, 48]`; `warpStrength ∈ [0, 1]`                             |
| `color-lut-match`           | non-empty `sourceLutId`/`targetLutId`; `matchFrames ∈ [1, 240]`              |
| `audio-crossfade`           | `fadeMs ∈ [50, 8000]`; `curve ∈ {linear, equal-power, log}`                  |
| `narrative-pivot`           | non-empty `pivotPromptId`; `resetMotionDescriptor` boolean                   |
| `motion-descriptor-handoff` | non-empty source/target descriptor ids; `blendFrames ∈ [1, 120]`             |
| `motion-descriptor-reset`   | `resetStrategy ∈ {clean-break, new-anchor}`; `holdFrames ∈ [0, 48]`          |
| `audio-level-jump`          | `deltaDb ∈ [-18, 18]`; `recoveryMs ∈ [0, 3000]`                              |
| `variable-rate-sequencer`   | non-empty `beatGridId`; `minRate ≥ 0.25`, `maxRate ≤ 4`, `minRate ≤ maxRate` |

`TransitionValidationError` carries a discriminated `code` of `'out-of-range'`,
`'unknown-kind'`, or `'missing-required'`, so callers can react to _why_ a
parameter was rejected, not just _that_ it was.

### Continuity Scorecard Gate

Independent of the per-technique catalog thresholds, the Blend Kernel exposes a
`ContinuityScorecard`
(`{ fvd, flickerScore, colorJump, motionVectorContinuity, audioRmsDeltaDb }`)
and `isBlendContinuityAcceptable(...)`. This gate first rejects any non-finite
metric, then checks each band (FVD/flicker/colorJump _below_ their max,
motionVectorContinuity _above_ its min, `|audioRmsDeltaDb|` below its max) and
returns `{ acceptable, reasons[] }`. The §25.18 release gates consume this: any
score below the configured threshold blocks promotion.

### Catalog Governance and Versioning

Every technique carries a `provenanceTag` recorded in the Render Envelope; the
audit trail on a kept artifact shows which techniques the AI assist selected vs.
which the user overrode. Catalog versions are pinned per artifact via
`PinnedTechnique { artifactId, techniqueId, catalogVersion }`; deprecation never
breaks playback, and `canReshareDeprecatedTechnique(...)` returns true only when
the pinned `catalogVersion` equals the current version — i.e. re-share of a
deprecated-technique artifact requires a re-render against the current catalog.

## Segment-Pair Compatibility Matrix

Composition is not free-form. The compatibility scorer (`scorer.ts`,
`scoreSegmentPair`) runs at composition time (when the user places a Segment)
and at runtime (before the Blend Kernel executes), and gates Technique Picker
availability. It is a **pure function over its inputs** — same pair → same
scores — which is what makes the determinism release gate testable.

### Scoring Dimensions

The scorer computes seven per-dimension scores in `[0, 1]`
(`COMPATIBILITY_DIMENSIONS`, `scorer.ts:21-29`); the **composite is the simple
mean of the seven** (`compositeOf`, summed and divided by 7):

| Dimension           | How it scores (real implementation)                                                                                                                                |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `style-anchor`      | **Exact ID equality**: `styleAnchorId === styleAnchorId ? 1 : 0.35`. _Not_ a CLIP-embedding distance (see [accuracy notes](#accuracy-notes-real-vs-aspirational)). |
| `motion-descriptor` | Exact equality: `1` if descriptors match, else `0.5`.                                                                                                              |
| `audio-role`        | Role-pair table: identical roles `1`; narration↔ambient `0.85`; music-bed↔silence `0.3` (harsh); any other mismatch `0.6`.                                         |
| `narration`         | `1` if `narrationTone` matches, else `0.5`.                                                                                                                        |
| `tone-band`         | `1` if `toneBand` matches, else `0.4`.                                                                                                                             |
| `grounding`         | Dice coefficient over `groundingSourcePinHashes` sets (both empty → `1`). Real overlap measure: `2·\|A∩B\| / (\|A\| + \|B\|)`.                                     |
| `persona`           | `1` if `personaId` matches, else `0.55`.                                                                                                                           |

The verdict is a discriminated union: `hard-incompatible` (policy block,
technique-agnostic), `soft-incompatible` (composite below
`compositeThresholdSoft` _or_ below `compositeThresholdCompatible`, with a
human-readable `rationale` like `composite 0.421 below soft threshold 0.6`), or
`compatible`. Malformed inputs (non-finite or inverted thresholds, empty
required fields) themselves return `hard-incompatible` with a reason — the
scorer fails loud rather than scoring garbage.

### Hard-Incompatible Pairs (Policy Gate)

`policyGate(...)` runs _before_ any dimensional scoring and short-circuits four
classes of pair regardless of mechanical compatibility:

1. **Crisis ↔ non-crisis** (`crisisLabel !== 'safe'` on exactly one side)
   _without_ a `fade-to-black` recovery transition → `hard-incompatible`, reason
   "crisis ↔ non-crisis pair requires recovery transition (fade-to-black)".
2. **Pedagogical ↔ entertainment** tone bands within the same Score → blocked.
3. **Tara contemplative ↔ forbidden technique**: when
   `templateId === 'tara-contemplative-arc'` and the chosen transition is
   `smash-cut`, `whip-pan`, or `jump-cut` → blocked.
4. **Veritas mutually-retracted pins**: under `veritas-grounded-explainer`, a
   pair where _both_ segments cite only Sophia source pins present in
   `retractedSophiaPinHashes` → blocked (no `CorrectionNote` bridge).

Soft-incompatible pairs are surfaced as warnings with the rationale string; the
user can override at AAA tier, never at customer tier, and overrides are
audited. Promoted Scores carry the final scores so audit and replay can
reconstruct why a technique was chosen.

## AI Compose Assist

The compose assist is a constrained agent that suggests next Segments, ranks
candidate cinematographic techniques per pair, and explains its reasoning. It
runs through Isis as an agent run with explicit budget caps, tool grants, and
operator audit (see
[Agent Invocation, Budgets, Memory, and Feedback Loops](./agentic-invocation-budgets-grants.md)).

- **Request shape** (`ComposeAssistRequest`):
  `{ requestId, templateId, locale, tier, priorSegmentSummary, budget, grants }`,
  where `tier` is one of
  `contemplative | curated-creator | aaa-creator | operator-admin`, `budget` is
  an `AgentRunBudget { maxTokens, maxCostCents, maxLatencyMs }`, and `grants` is
  `AgentRunGrants { toolIds[], allowEgress[] }`.
- **Suggestion shape** (`ComposeAssistSuggestion`): a `candidateSegment`
  (`styleAnchorId`, `motionDescriptor`, `audioRole`, `toneBand`), an ordered
  `rankedTechniques` list where each `TechniqueSuggestion` carries a
  `confidence: [low, high]` band, a `rationale` string, and a
  `continuityPreScore`.
- **Budget enforcement**: `isBudgetExceeded(...)` aborts the run the instant any
  observed metric (tokens / cost cents / latency ms) crosses its cap. Every
  assist call therefore produces an audit-reconstructable envelope.
- **Gating of suggestions**: `filterComposeAssistTechniqueSuggestions(...)` runs
  every ranked technique through `templateAllowsCompositionTechnique(...)`, so
  the assist can only _offer_ a technique the destination template, crisis
  state, Metis-assessment lock, and Arete operator grant all permit.
- **Soft refusal is mandatory**: `softRefuse(reason)` returns a
  `{ kind: 'soft-refusal', explanation }` verdict — the customer-tier promise is
  that the assist must produce a viable suggestion or _explicitly_ decline
  ("there isn't a clean bridge from here yet — try a different anchor"); silent
  failure is forbidden.
- **Champion–challenger gold sets**: assist quality is tracked with
  `evaluateGoldSet(...)`, which returns
  `{ precision, recall, refusalRate, correctRefusals, falseRefusals }`.
  Precision is `tp/(tp+fp)`, recall is `tp/(tp+fn)` (both default to `1` on
  empty denominators), and `correctRefusals` counts cases where a "should
  refuse" item was correctly declined, while `falseRefusals` counts viable
  bridges wrongly refused. `gateChampionChallenger(...)` promotes a challenger
  only when it meets the floor on all three top-line metrics _and_ is no worse
  than the champion on each. Fixtures include high-compatibility golden paths,
  low-compatibility "should refuse" pairs, and adversarial intent traces that
  try to bypass tone gating, evaluated across V1 launch locales and per
  template.

## Per-Template Availability and Tone Gating

The catalog is identical across templates, but each template publishes its own
**technique allowlist** and **tone gating policy**. The Customer Technique
Picker only surfaces allowed entries. Compose Assist only considers allowed
entries. The AAA-tier Scene Score Editor surfaces all entries but blocks
promotion of a Score that uses a disallowed technique for the target template.
The five canonical machine template IDs are `tara-contemplative-arc`,
`nyx-sky-briefing`, `veritas-grounded-explainer`, `metis-lesson-visualizer`, and
`arete-living-offering` (the friendly names "Tara Contemplative Arcs," etc., are
display strings; the IDs above are the keys used in code).

| Technique       | Tara | Nyx | Veritas | Metis | Arete |
| --------------- | :--: | :-: | :-----: | :---: | :---: |
| `hard-cut`      |  ✓   |  ✓  |    ✓    |   ✓   |   ✓   |
| `match-cut`     |  ✓   |  ✓  |    —    |   ✓   |   ✓   |
| `l-cut`         |  ✓   |  —  |    ✓    |   ✓   |   ✓   |
| `j-cut`         |  ✓   |  —  |    ✓    |   ✓   |   ✓   |
| `dissolve`      |  ✓   |  ✓  |    ✓    |   ✓   |   ✓   |
| `match-action`  |  ✓   |  ✓  |    —    |   ✓   |   ✓   |
| `cross-cut`     |  —   |  —  |    ✓    |   ✓   |   ✓   |
| `montage`       |  —   |  —  |    —    |   ✓   |   ✓   |
| `fade-to-black` |  ✓   |  ✓  |    ✓    |   ✓   |   ✓   |
| `whip-pan`      |  —   |  ✓  |    —    |   —   |   ◆   |
| `smash-cut`     |  —   |  —  |    —    |   —   |   ◆   |
| `jump-cut`      |  —   |  —  |    —    |   —   |   ◆   |

✓ allowed · — disallowed · ◆ **operator-curated only** (Arete).

The ◆ entries are the `OPERATOR_CURATED_ARETE_TECHNIQUES` set
(`{smash-cut, whip-pan, jump-cut}`). For Arete, these three require
`operatorCuratedTechniqueGrant === true` **and**
`strictestLilithToneBand !== true` to be admitted; under the strictest Lilith
tone band they are blocked outright. (`whip-pan` also appears on Nyx's standard
allowlist, for "pan across the sky" — it is operator-curated only on Arete.) The
same allowlist data is mirrored in two places that must stay in sync: the
per-technique `templateAllowlist[]` in `cinematographic-catalog.ts` and the
`TEMPLATE_TECHNIQUE_ALLOWLISTS` table in `compose-assist.ts`. They currently
agree for all five templates, but they are a duplicated source of truth that can
drift — a known maintenance hazard.

### Enforcement Points and Crisis Collapse

`assertTechniqueAllowed(...)` (`tone-gating.ts`) is enforced at three
`ENFORCEMENT_POINTS` — `customer-picker`, `compose-assist`, and
`score-promotion` — and evaluates gates in a deliberate precedence order:

1. **Crisis-frame collapse takes precedence over everything.** When
   `inCrisisFrame` is true, the allowlist collapses to the
   `CRISIS_COLLAPSE_ALLOWLIST` — `{hard-cut, dissolve, fade-to-black}` — for
   _every_ template; anything else throws `crisis-frame-collapse`. The tests
   require this to happen within 1 frame of crisis-frame activation, and Compose
   Assist explicitly declines to suggest a continuation.
2. **Themis interlock for Metis.** When the template is
   `metis-lesson-visualizer` and the Segment's node is in
   `metisAssessmentLockedNodes`, _every_ technique is blocked
   (`themis-assessment-locked`) — you cannot cut into or out of an in-progress
   assessment item.
3. **Per-template allowlist** — disallowed technique →
   `technique-not-in-template-allowlist`.
4. **Arete operator-curated guard** — strictest Lilith tone →
   `strictest-lilith-tone-block`; missing grant →
   `operator-curated-technique-required`.

Tara additionally locks its tone-band swing to 0 (`strictestToneBand: true` in
the template catalog), which is why `match-cut`/`match-action` are admitted only
in their gentle forms and the high-energy techniques never are.

## The Conductor Runtime State Machine

A promoted Score is streamed by the Conductor
(`@yemaya/living-scenes-runtime/conductor/conductor.ts`). The Conductor is
**pure scheduling logic**; its header is explicit: _"Actual GPU dispatch lives
outside this module."_ It models each Segment as a `SegmentSlot` whose
`SegmentRenderState` walks a fixed lifecycle:

```
pending → pre-warming → ready → streaming → done
                                          ↘ aborted
```

| Function                                              | Role                                                                                                                                                                                                                                                                                      |
| ----------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `initialiseConductor({ score })`                      | Builds one `SegmentSlot` per Segment, all `pending`, seeds each slot's `carryStateInbound` from the Segment's `inboundCarryState`, playhead at 0.                                                                                                                                         |
| `planPreWarm({ state, nowUnixSeconds })`              | Marks pending slots in the window `[playheadIdx+1, playheadIdx+minLookaheadSegments]` as `pre-warming`. **If `backpressureActive`, the lookahead truncates to 1** — only the immediate next slot pre-warms.                                                                               |
| `completePreWarm({ state, segmentId, ... })`          | `pre-warming → ready`; throws `invalid-transition` if the slot is in any other state.                                                                                                                                                                                                     |
| `advancePlayhead({ state, carryStateOutbound, ... })` | Marks the current slot `done` with its outbound carry-state, then promotes the next slot to `streaming` — but only if it is already `ready` or `streaming`, else throws `invalid-transition`. Carry-state propagates: `carryStateInbound = carryStateOutbound ?? next.carryStateInbound`. |
| `setBackpressure({ state, active })`                  | Toggles the backpressure flag that throttles lookahead.                                                                                                                                                                                                                                   |
| `planReconnect({ state, atSegmentId })`               | Mid-segment reconnect: returns `{ resumable, resumeCarryState, reason }`; resumable only if the slot is `streaming` or `ready`, in which case it hands back that slot's inbound carry-state for the client to resume from.                                                                |

`ConductorError` codes are `invalid-playhead`, `invalid-transition`,
`missing-carry-state`, `segment-not-found`.

### Carry-State Handoff

The continuity protocol the Conductor threads between Segments is the
`CarryState` schema (`score.ts:38-44`), and it maps almost one-to-one onto the
"CLIP-style style anchor / last-frame conditioning / motion-vector descriptor /
color-LUT alignment / audio tail" the customer promise describes — but with
concrete field names and constraints:

| Field                       | Constraint                                   |
| --------------------------- | -------------------------------------------- |
| `clipAnchorAssetId`         | non-empty string                             |
| `lastFrameConditioningHash` | matches `/^[0-9a-f]{32,}$/` (≥ 32 hex chars) |
| `motionDescriptor`          | 1–120 chars                                  |
| `lutId`                     | non-empty string                             |
| `audioTailDescriptor`       | 1–120 chars                                  |

### Render Envelope Pins the Reproducibility Frame

`planPreWarm` reads `minLookaheadSegments` straight off the `RenderEnvelope`
(`score.ts:91-107`), which is far richer than "engine version + model hashes"
prose. Every field is a _literal_ union, so an out-of-range value fails at parse
time:

| Field                       | Type / allowed values                                     |
| --------------------------- | --------------------------------------------------------- |
| `envelopeId`, `displayName` | strings (≤ 120 chars for display name)                    |
| `widthPx`                   | literal `1080 \| 1440 \| 1920 \| 2560 \| 3840`            |
| `heightPx`                  | positive int ≤ 7680                                       |
| `fps`                       | literal `24 \| 30 \| 60`                                  |
| `maxBitrateKbps`            | positive int                                              |
| `gpuClass`                  | literal `rtx-4090 \| a100-40gb \| a100-80gb \| h100-80gb` |
| `minLookaheadSegments`      | literal `2 \| 3 \| 4`                                     |

The lookahead is also a **release-gate invariant**: `deepParseScore`
(`score.ts:151-168`) re-checks `minLookaheadSegments < 2` after schema parsing
and throws _"renderEnvelope must require ≥ 2 lookahead segments"_ — a
belt-and-suspenders guard so no Score can stream with too little pre-warm
headroom even if the literal union were widened. `deepParseScore` also enforces
segment-id uniqueness and that every cue's `appliesToSegmentId` resolves to a
real segment (or the wildcard `*`).

## Live Direction Channel Telemetry — Scene Events

The Conductor's decisions and the policy interventions around them surface to
the viewer and the Score editor as a uniform, traceable event feed on the Psyche
envelope (`scene-events.ts`). There are **eight** `SCENE_EVENT_KINDS`:

| Event                          | Payload highlights                                                                                                                                                                                                  |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `scene.segment-start`          | `segment` ref, `plannedDurationSeconds`, `engineVersion`, `workflowClassVersion`                                                                                                                                    |
| `scene.segment-end`            | `actualDurationSeconds`, `endReason ∈ {planned, cue-advance, cue-abandon, policy-cut}`                                                                                                                              |
| `scene.transition-start`       | `fromSegment`/`toSegment`, `techniqueId`, `overlapWindowSeconds`                                                                                                                                                    |
| `scene.transition-end`         | `techniqueId` + a `continuityScorecard { fvd, flicker, colorJump, motionVector }`                                                                                                                                   |
| `scene.live-direction-applied` | `cueVerb`, `cueArgs`, `cueChannel ∈ {voice, tap, text}`, target segment                                                                                                                                             |
| `scene.policy-intervention`    | `interventionKind ∈ {pse-strobe-cap, luminance-cap, motion-cap, tone-band-shift, persona-cap}`, pre/post values, `policyVersion`                                                                                    |
| `scene.crisis-frame`           | `crisisLabel` (8 labels from `safe` … `self-harm-imminent` … `crisis-other`), `probability`, `enteredAtUnixMillis`                                                                                                  |
| `scene.fallback-engaged`       | `fallbackKind ∈ {reduced-motion-render, still-image-only, transcript-only, audio-with-still, pre-rendered-substitute}`, `trigger ∈ {provider-degraded, policy-rejection, eval-gate-fail, shareability-restriction}` |

`verifyEventStream(...)` enforces a **monotonic, gap-free sequence** (each
`sequence` must be exactly `last + 1`) and a **single trace-id** across the
whole stream (a mid-stream trace-id change throws `trace-id-missing`), so audit
replay can prove the feed is complete and un-interleaved.

## Continuity-Eval Extensions for Named Techniques

The base Continuity Eval suite (FVD, flicker, color-jump, motion-vector,
narration-time) generalizes across all transitions. Named techniques
_supplement_ those with their own catalog `evalThresholds` (the table in
[V1 Launch Catalog](#v1-launch-catalog--kernel-composition-and-eval-thresholds))
— they never replace the base evals:

- **`match-cut`** carries the tightest `maxFvd` (0.30) and
  `maxMotionVectorDelta` (0.30) of the cinematic cuts, reflecting its
  shape/motion-continuity demand.
- **`l-cut` / `j-cut`** demand the highest narration alignment (0.94), because
  audio leads or lags the visual and captions must align to the audio boundary
  (their `accessibilityNote`s spell out which side captions follow).
- **`whip-pan`** allows the loosest motion-vector delta (0.70) because the
  technique _is_ high-magnitude motion; under reduced motion it falls back to
  `fade-to-black`. A too-gentle whip pan is, by intent, the wrong technique.
- **`jump-cut`** allows the loosest FVD (0.60); it is restricted to cinematic /
  entertainment bands and forbidden in contemplative and pedagogical bands.
- **`montage`** is the only technique whose reduced-motion fallback is
  `still-frame`, and its `accessibilityNote` requires audio description per
  visual change.
- **`fade-to-black`** verifies the tightest flicker (0.03) and a high narration
  alignment (0.92), since it is the section-break / recovery transition.

Per-technique gates are enforced at composition-draft promotion and at runtime
before the Blend Kernel executes; below-threshold transitions surface as
warnings at customer tier or block promotion at AAA tier, and every failure is
logged with the failing metric so Compose Assist can learn from it.

## Tara Sub-Variants — A Worked Example

The Tara Contemplative Arc template ships five sub-variants (`TARA_SUBVARIANTS`,
`template-catalog.ts:18-24`), each with a concrete, deterministic `BreathCycle`
(`tara.ts`) of
`{ inhaleSeconds, holdInSeconds, exhaleSeconds, holdOutSeconds }`:

| Sub-variant        | Inhale | Hold-in | Exhale | Hold-out |     Cycle total      |
| ------------------ | :----: | :-----: | :----: | :------: | :------------------: |
| `loving-kindness`  |   4    |    1    |   6    |    1     |         12 s         |
| `body-scan`        |   4    |    0    |   8    |    0     |         12 s         |
| `breath-anchor`    |   4    |    4    |   4    |    4     | 16 s (box breathing) |
| `nature-immersion` |   5    |    0    |   7    |    0     |         12 s         |
| `gratitude`        |   4    |    1    |   6    |    1     |         12 s         |

`breathCycleSeconds(...)` simply sums the four phases, and the Tara template
constrains its allowed cue verbs to
`{linger, advance, save-moment, abandon, kill}` with `strictestToneBand: true`
and `reducedMotionVariantRequired: true`. This is why the composition surface
for a Tara Score offers only the gentle technique set and locks tone-band swing
to 0.

## Eval Gates and Tests

Every template is held to a fixture-based eval gate (`TEMPLATE_EVAL_GATES`,
`template-catalog.ts:147-199`). All five require a `minimumFixtures` of **30**,
a `crisisTrigger` pass rate of **1.0** (no crisis trigger may ever be missed),
and a `localeParity` floor of 0.90; the rest vary by template:

| Template                     | golden | adversarialCue | accessibilityParity | groundingCorrectness |
| ---------------------------- | :----: | :------------: | :-----------------: | :------------------: |
| `tara-contemplative-arc`     |  0.95  |      0.98      |        0.95         |       — (none)       |
| `nyx-sky-briefing`           |  0.95  |      0.97      |        0.95         |         0.92         |
| `veritas-grounded-explainer` |  0.97  |      0.99      |        0.97         |         0.97         |
| `metis-lesson-visualizer`    |  0.95  |      0.97      |        0.97         |         0.95         |
| `arete-living-offering`      |  0.95  |      0.99      |        0.95         |       — (none)       |

Veritas carries the strictest golden, adversarial, and grounding thresholds,
reflecting its claim-grounding burden; Tara and Arete have no grounding gate
(`groundingCorrectness: null`) because they are not source-pinned.

The test coverage backing this page (across the libraries above and the BFF):

- Catalog round-trip and contract conformance across all twelve entries; the
  twelve `provenanceTag`s are grep-confirmed.
- Compatibility-scorer determinism (same pair → same scores), since it is a pure
  function.
- Per-template allowlist enforcement at all three enforcement points; every
  disallowed technique is rejected by the Technique Picker, Compose Assist, and
  Score promotion.
- Crisis-frame collapse to `{hard-cut, dissolve, fade-to-black}` within one
  frame.
- AI Compose Assist gold-set evaluation (`precision`, `recall`, `refusalRate`,
  `correctRefusals`, `falseRefusals`) across V1 launch locales.
- Hard-incompatible pair coverage (every documented pair detected and blocked).
- Reduced-motion fallback substitution per the
  `REDUCED_MOTION_TECHNIQUE_FALLBACKS` table.
- Scene-event stream verification: monotonic gap-free sequence + single
  trace-id.
- The customer scene viewer and BFF:
  `apps/oshun/bff/src/routes/living-scenes.ts` is ~1,871 lines of real keep /
  share / idempotency logic, exercised by `scene-viewer-deepening.spec.ts`,
  `living-scene-card.spec.ts`, and `public-scene-abuse-report.spec.ts` (per the
  v1 triage walkthrough).
- Determinism: a composed Score re-rendered under the same Envelope produces a
  pixel-equivalent video within Isis-defined tolerance — a release gate (§25.9).

## Accuracy Notes (Real vs. Aspirational)

Two points where the older prose overstated the implementation, corrected here
so the page matches the code:

- **Style-anchor "distance" is exact-ID equality, not CLIP-embedding distance.**
  The real `styleScore` (`scorer.ts:75-77`) returns `1` when `styleAnchorId`
  matches and `0.35` otherwise; `motionScore` is likewise `1` vs `0.5` exact
  equality. The "CLIP-region embedding similarity ≥ 0.78" and "motion-vector
  alignment ≥ 0.80" figures the prose attaches to `match-cut`/`match-action` are
  **catalog eval thresholds the downstream renderer is checked against**, not
  embedding distances computed in this pure module. The compatibility scorer is
  deliberately string-grounded so it stays deterministic and dependency-free.
- **Segment fields are not the 13-field prose list.** The real `SegmentSpec`
  (`score.ts:47-60`) is
  `{ segmentId, kind, displayName, durationSeconds, tone, workflowClassId, parameters, inboundCarryState }`.
  `intent` lives on the _Score_ (`ScoreIntentLayer`, with
  private/public-redacted text and a redaction-category list), not the Segment.
  Transitions are not stored on Segments (they are decided at boundaries).
  Grounding, persona, and accessibility role are not Segment fields. See
  [Scene Score Schema](./scene-score-schema.md) for the full authoritative
  schema.

The entire pixel/frame layer — actual latent video generation, optical-flow
warps, FVD computation, GPU-deterministic rendering — is **aspirational relative
to these modules**: it is modeled as score inputs the pure functions consume,
and its implementation lives downstream of the contracts documented here. Honest
"planned/gated downstream" is the accurate posture; the decision logic on this
page is shipped and tested.

## Related

- [Living Scenes — Concept and Customer Promise](./living-scenes-overview.md)
- [Scene Score Schema](./scene-score-schema.md)
- [Live Direction, Conductor Runtime, and Blend Kernel](./direction-conductor-blend.md)
- [Domain Templates and the Scene Score Editor](./domain-templates-and-editor.md)
- [Scene Safety, Determinism, Provenance, and Cue Privacy](./scene-governance.md)
- [Latency, Accessibility, Eval Sets, and Tests](./scene-quality-and-eval.md)
- [Agent Invocation, Budgets, Memory, and Feedback Loops](./agentic-invocation-budgets-grants.md)
- [Sophia Grounding](./sophia-grounding.md)
- [Lilith Persona Policy](./lilith-persona-policy.md)
- Feature hub: [../features.md](../features.md)
