motion-choreographer
Turn an approved still + the 12-block scene blueprint into a
provider-tuned motion prompt that the target video adapter
consumes. Camera choreography, primary subject motion, secondary
environment motion, physics constraints, and — when the adapter
declares audio: native — a synchronized audio direction block.
Reads adapter capabilities from
adapter-contract.md;
never speaks to a network API.
When to use
- An image is locked (operator picked one candidate via
operator-pick.sh) and the next step is motion + audio direction
for the video adapter.
- The blueprint exists in
scenes/<id>/blueprint.json but the
motion prompt has not been emitted yet.
- A provider switch (Veo → Kling, Sora → Higgsfield) requires the
same scene retuned for the new adapter's capability profile.
Do NOT use when:
- The blueprint is still prose only — run
scene-expander → parse-blueprint.sh
first.
- No still has been locked — the operator-selection checkpoint
must complete first.
- The output is a still graphic —
canvas-design.
Procedure
Step 0: Inspect
- Read
scenes/<id>/blueprint.json — fail loud if missing.
- Read
scenes/<id>/selection.json — fail loud if missing; the
locked image path is required as the motion anchor.
- Read the target adapter's capability via
scripts/ai-video/adapters/<id>.sh capability. Cache audio=*
for Step 3.
- If a
character.json lock exists, load it verbatim — identity
tokens are immutable.
- Continuous take or connector asked for? Read the model's
start_frame / end_frame first —
scripts/ai-video/adapters/<id>.sh capability --model <m>. null is
unknown, never true. A model that cannot frame-lock is declined
with a one-line why, never substituted in
(adapter-contract.md#end_image).
Step 1: Camera choreography
Emit a CAMERA MOTION block with the move type, distance, speed
in seconds, and start-end framing.
- Move types: lock-off, pan, tilt, dolly-in, dolly-out, truck,
pedestal, push, pull, handheld, gimbal-glide, crane, whip.
- Speed in seconds per beat (
0.4s push, hold 1.6s, 0.4s pull).
- Start and end framing named (
MS → CU, WS → MS).
Blueprint intent-class decoding — the blueprint CAMERA block names
one of six provider-agnostic intent classes; map it to the move types
above before encoding:
| Intent class |
Move-type mapping |
| static hold |
lock-off |
| push-in |
push / dolly-in |
| pull-back |
pull / dolly-out |
| lateral track |
truck / pan |
| handheld drift |
handheld / gimbal-glide |
| orbit |
crane / arc via truck+pan |
Adapter quirks (per-provider encoding of the mapped move):
- Veo — accepts named moves; prefers ≤ 8s clips.
- Kling — motion intensity 0–1 token; map our speed to that.
- Sora — natural-language move + duration; no token.
- Higgsfield — preset-driven; pick the preset that matches the
move; record the preset id in the motion prompt.
- LENS handling — blueprint mm + aperture translate to the
provider's nearest lens / FOV idiom; never forward raw mm to a
provider that lacks lens grammar.
- DURATION handling — the blueprint value is free; clamp to the
provider ceiling here and record the clamp in
review.md (a
silent clamp is a drift bug).
Step 2: Primary + secondary motion
Two blocks:
- PRIMARY MOTION — what the subject does, beat-counted, with
physics anchors (mass, contact points, momentum). Reuse
ACTION
from the blueprint; refine for the adapter's preferred verb
density.
- SECONDARY MOTION — what the world does (hair, fabric,
foliage, water, dust, particles, breath). One layer per line.
Step 3: Audio direction (conditional)
If adapter capability is audio: native AND the blueprint's
audio.enable_native_audio is true:
Emit an AUDIO DIRECTION block with:
DIALOGUE TIMING — speaker @ 0.4s: "line" per dialogue entry.
AMBIENT LAYERS — copy from blueprint; one layer per line.
SYNC CUES — which action beat maps to which audio cue
(footstep @ 1.2s, door close @ 2.1s).
If adapter capability is audio: none:
- Emit a
# AUDIO: ffmpeg-mux fallback comment with the
blueprint's audio paths queued for stitch-time mux.
- Set
enable_native_audio: false in the motion-prompt JSON.
- Blueprint carries DIALOGUE → the mux fallback cannot speak text
lines. Emit a mandatory
AUDIO DOWNGRADE warning block in
motion-prompt.txt AND adapter-notes.md listing every dialogue
line that will not render. The decision whether that is acceptable
belongs to the orchestrator gate (adapter-contract § Audio
ownership) — this skill translates, it never validates, and it
NEVER silently strips dialogue.
Step 4: Physics constraints
Emit PHYSICS — a short list of what the model must respect:
gravity direction, contact friction, fluid behavior, hair / cloth
inertia, lens parallax. Single line per constraint.
Step 5: Emit motion-prompt JSON
Write scenes/<id>/motion-prompt.json with the adapter-contract
stdin shape. The orchestrator pipes this into the video adapter's
submit subcommand.
Step 6: Validate
- JSON parses (
jq .).
requires.audio_native is consistent with the chosen adapter's
capability.
- Duration in the motion prompt matches blueprint duration ±0.
- Identity tokens (if
character.json exists) are verbatim.
Output format
scenes/<id>/motion-prompt.json — adapter-contract stdin.
scenes/<id>/motion-prompt.txt — labeled prose blocks
(CAMERA MOTION · PRIMARY MOTION · SECONDARY MOTION · AUDIO
DIRECTION · PHYSICS) for operator review.
scenes/<id>/adapter-notes.md — which adapter, which
capability, which preset / model, with rationale.
Gotcha
- The model wants to "improve" the blueprint's
SUBJECT block —
identity tokens are immutable; refuse the temptation.
- Picking
audio: native on an adapter that returns audio: none
produces silent video — always read capability first, never
guess from the adapter name.
- Higgsfield preset id must be recorded; otherwise the rerun
drifts to whichever preset the model picks on the next call.
- Sora durations > 8s often degrade — clamp at the adapter table
limit; surface the clamp to the operator.
Do NOT
- Do NOT emit motion prompts for an adapter whose capability you
did not query this turn.
- Do NOT skip the still-locked check — motion direction without an
anchored image diverges on every call.
- Do NOT paraphrase identity tokens from
character.json.
- Do NOT call any network API — this skill is provider-tuning
prose only.
Policies
Paths, enforcement model, and the full set: the
media policy preamble.
Motion prompts inherit every constraint the upstream blueprint carries. Before emitting provider-tuned prose:
disclosure — every distributed clip carries the non-removable AI-generation disclosure; refuse adapter flags that would suppress it.
transparency — provider provenance (C2PA / SynthID) is preserved; refuse re-encode flags whose effect is to strip provenance.
voice-cloning — when the motion prompt requests audio: native narration in a named voice.
brand-impersonation — when the motion prompt copies a recognised brand's chyron / mascot / signature transition.
Refuse-and-surface; the motion prompt cannot launder a policy gap upstream skills should have caught.
1---2name: motion-choreographer3description: When turning a locked still + blueprint into a provider-tuned motion prompt — camera, primary + secondary motion, physics, native-audio sync. Triggers 'motion prompt for Veo/Kling/Sora'.4---56# motion-choreographer78> Turn an approved still + the 12-block scene blueprint into a9> provider-tuned **motion prompt** that the target video adapter10> consumes. Camera choreography, primary subject motion, secondary11> environment motion, physics constraints, and — when the adapter12> declares `audio: native` — a synchronized audio direction block.13> Reads adapter capabilities from14> [`adapter-contract.md`](../../../scripts/media/lib/adapter-contract.md);15> never speaks to a network API.1617## When to use1819- An image is locked (operator picked one candidate via20 `operator-pick.sh`) and the next step is motion + audio direction21 for the video adapter.22- The blueprint exists in `scenes/<id>/blueprint.json` but the23 motion prompt has not been emitted yet.24- A provider switch (Veo → Kling, Sora → Higgsfield) requires the25 same scene retuned for the new adapter's capability profile.2627Do NOT use when:2829- The blueprint is still prose only — run30 [`scene-expander`](../scene-expander/SKILL.md) → `parse-blueprint.sh`31 first.32- No still has been locked — the operator-selection checkpoint33 must complete first.34- The output is a still graphic — `canvas-design`.3536## Procedure3738### Step 0: Inspect39401. Read `scenes/<id>/blueprint.json` — fail loud if missing.412. Read `scenes/<id>/selection.json` — fail loud if missing; the42 locked image path is required as the motion anchor.433. Read the target adapter's capability via44 `scripts/ai-video/adapters/<id>.sh capability`. Cache `audio=*`45 for Step 3.464. If a `character.json` lock exists, load it verbatim — identity47 tokens are immutable.485. Continuous take or connector asked for? Read the model's49 `start_frame` / `end_frame` first —50 `scripts/ai-video/adapters/<id>.sh capability --model <m>`. `null` is51 unknown, never `true`. A model that cannot frame-lock is **declined52 with a one-line why**, never substituted in53 ([`adapter-contract.md#end_image`](../../../scripts/media/lib/adapter-contract.md#end_image)).5455### Step 1: Camera choreography5657Emit a `CAMERA MOTION` block with the move type, distance, speed58in seconds, and start-end framing.5960- Move types: lock-off, pan, tilt, dolly-in, dolly-out, truck,61 pedestal, push, pull, handheld, gimbal-glide, crane, whip.62- Speed in seconds per beat (`0.4s push, hold 1.6s, 0.4s pull`).63- Start and end framing named (`MS → CU`, `WS → MS`).6465Blueprint intent-class decoding — the blueprint CAMERA block names66one of six provider-agnostic intent classes; map it to the move types67above before encoding:6869| Intent class | Move-type mapping |70|---|---|71| static hold | lock-off |72| push-in | push / dolly-in |73| pull-back | pull / dolly-out |74| lateral track | truck / pan |75| handheld drift | handheld / gimbal-glide |76| orbit | crane / arc via truck+pan |7778Adapter quirks (per-provider encoding of the mapped move):7980- **Veo** — accepts named moves; prefers ≤ 8s clips.81- **Kling** — motion intensity 0–1 token; map our speed to that.82- **Sora** — natural-language move + duration; no token.83- **Higgsfield** — preset-driven; pick the preset that matches the84 move; record the preset id in the motion prompt.85- **LENS handling** — blueprint mm + aperture translate to the86 provider's nearest lens / FOV idiom; never forward raw mm to a87 provider that lacks lens grammar.88- **DURATION handling** — the blueprint value is free; clamp to the89 provider ceiling here and record the clamp in `review.md` (a90 silent clamp is a drift bug).9192### Step 2: Primary + secondary motion9394Two blocks:95961. **PRIMARY MOTION** — what the subject does, beat-counted, with97 physics anchors (mass, contact points, momentum). Reuse `ACTION`98 from the blueprint; refine for the adapter's preferred verb99 density.1002. **SECONDARY MOTION** — what the world does (hair, fabric,101 foliage, water, dust, particles, breath). One layer per line.102103### Step 3: Audio direction (conditional)104105If adapter capability is `audio: native` AND the blueprint's106`audio.enable_native_audio` is `true`:107108Emit an `AUDIO DIRECTION` block with:109110- `DIALOGUE TIMING` — `speaker @ 0.4s: "line"` per dialogue entry.111- `AMBIENT LAYERS` — copy from blueprint; one layer per line.112- `SYNC CUES` — which action beat maps to which audio cue113 (`footstep @ 1.2s`, `door close @ 2.1s`).114115If adapter capability is `audio: none`:116117- Emit a `# AUDIO: ffmpeg-mux fallback` comment with the118 blueprint's audio paths queued for stitch-time mux.119- Set `enable_native_audio: false` in the motion-prompt JSON.120- Blueprint carries DIALOGUE → the mux fallback cannot speak text121 lines. Emit a mandatory `AUDIO DOWNGRADE` warning block in122 `motion-prompt.txt` AND `adapter-notes.md` listing every dialogue123 line that will not render. The decision whether that is acceptable124 belongs to the orchestrator gate (adapter-contract § Audio125 ownership) — this skill translates, it never validates, and it126 NEVER silently strips dialogue.127128### Step 4: Physics constraints129130Emit `PHYSICS` — a short list of what the model must respect:131gravity direction, contact friction, fluid behavior, hair / cloth132inertia, lens parallax. Single line per constraint.133134### Step 5: Emit motion-prompt JSON135136Write `scenes/<id>/motion-prompt.json` with the adapter-contract137stdin shape. The orchestrator pipes this into the video adapter's138`submit` subcommand.139140### Step 6: Validate1411421. JSON parses (`jq .`).1432. `requires.audio_native` is consistent with the chosen adapter's144 capability.1453. Duration in the motion prompt matches blueprint duration ±0.1464. Identity tokens (if `character.json` exists) are verbatim.147148## Output format1491501. **`scenes/<id>/motion-prompt.json`** — adapter-contract stdin.1512. **`scenes/<id>/motion-prompt.txt`** — labeled prose blocks152 (CAMERA MOTION · PRIMARY MOTION · SECONDARY MOTION · AUDIO153 DIRECTION · PHYSICS) for operator review.1543. **`scenes/<id>/adapter-notes.md`** — which adapter, which155 capability, which preset / model, with rationale.156157## Gotcha158159- The model wants to "improve" the blueprint's `SUBJECT` block —160 identity tokens are immutable; refuse the temptation.161- Picking `audio: native` on an adapter that returns `audio: none`162 produces silent video — always read capability first, never163 guess from the adapter name.164- Higgsfield preset id must be recorded; otherwise the rerun165 drifts to whichever preset the model picks on the next call.166- Sora durations > 8s often degrade — clamp at the adapter table167 limit; surface the clamp to the operator.168169## Do NOT170171- Do NOT emit motion prompts for an adapter whose capability you172 did not query this turn.173- Do NOT skip the still-locked check — motion direction without an174 anchored image diverges on every call.175- Do NOT paraphrase identity tokens from `character.json`.176- Do NOT call any network API — this skill is provider-tuning177 prose only.178179## Policies180181Paths, enforcement model, and the full set: the182[media policy preamble](../../../agents/settings/policies/media/README.md).183184Motion prompts inherit every constraint the upstream blueprint carries. Before emitting provider-tuned prose:185186- **`disclosure`** — every distributed clip carries the non-removable AI-generation disclosure; refuse adapter flags that would suppress it.187- **`transparency`** — provider provenance (C2PA / SynthID) is preserved; refuse re-encode flags whose effect is to strip provenance.188- **`voice-cloning`** — when the motion prompt requests `audio: native` narration in a named voice.189- **`brand-impersonation`** — when the motion prompt copies a recognised brand's chyron / mascot / signature transition.190191Refuse-and-surface; the motion prompt cannot launder a policy gap upstream skills should have caught.192