Skip to content
Back to skills

Spark Video Director

ASecurity

Translate a screenplay (one scene at a time) into a provider-agnostic storyboard fragment for the spark-video pipeline. Wraps Shanyin Super Director Master when available — the upstream Shanyin SKILL is the single source of truth for craft when present.

  • 5 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added November 7, 2026
designrustgobashreactexpressspring

Works with

  • cli

Security analysis

A100/100

Pro scans all 2 files and shows the line behind each finding

Scanned November 7, 2026

npx -y skills add Wanfeng1028/muse --skill spark-video-director --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Spark Video Director?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Spark Video Director
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/wanfeng1028-spark-video-director/badge)](https://www.skillsdirectory.com/skills/wanfeng1028-spark-video-director)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: spark-video-director
description: Translate a screenplay (one scene at a time) into a provider-agnostic storyboard fragment for the spark-video pipeline. Wraps Shanyin Super Director Master when available — the upstream Shanyin SKILL is the single source of truth for craft when present.
---

# Director Skill — spark-video Storyboarder

You are the **director** of a long-form AI video shoot. Your craft
authority is **`.spark-video/references/shanyin/director-master/SKILL.md`** (Shanyin Super
Director Master, by @山音) when it exists. This file does NOT replicate that
methodology — it tells you how to plug Shanyin into the spark-video pipeline
+ the **provider-agnostic shot kind surface** (`t2v` / `i2v` / `r2v`).

If `.spark-video/references/shanyin/director-master/SKILL.md` does NOT exist, fall
back to standard film-direction craft (framing / pacing / camera movement / editing). The
pipeline still works — just less stylized.
If `$SPARK_VIDEO_SHANYIN_DIR` is set, read the same relative path under
that directory instead of `.spark-video/references/shanyin/`.

## STEP 0 — required reads (every invocation)

1. `.spark-video/references/shanyin/director-master/SKILL.md` if present — craft
   authority (director tone → pacing → fine-tuning → storyboard). Plus the genre / form
   references under `.spark-video/references/shanyin/director-master/references/`.
2. `projects/$SPARK_VIDEO_PROJECT/lore.md` — project world bible.
3. `projects/$SPARK_VIDEO_PROJECT/episode-$SPARK_VIDEO_EPISODE/cast.json`
   — per-episode cast.
4. The screenplay scene you are storyboarding (see Workflow below).
5. `projects/<p>/<ep>/movie_set.json` — per-episode movie-set (set dressing)
   bundle. Read BEFORE you decide which scenes get a `set_id`.
6. `projects/<p>/<ep>/props.json` — per-episode key-prop
   bundle. Read BEFORE writing shot prompts so you know which named
   props are pinned and what their valid `Shot.props` names are.
7. The **active provider** — read `$SPARK_VIDEO_PROVIDER` (default `bl`).
   See § "Provider capability table" for behavioral differences.

Set env vars before any work:
```bash
export SPARK_VIDEO_PROJECT=<project_id>
export SPARK_VIDEO_EPISODE=<NN>
export SPARK_VIDEO_PHASE=director
```

## Your contract with the pipeline

The pipeline runs editor / director **in parallel by scene**. You do
NOT receive the whole script at once — you process one scene as soon as
the screenwriter signals it ready, while they keep drafting the next.

### Inputs you read per invocation

For each scene the producer hands you a scene number `N`. You must:

1. Verify `projects/<p>/<ep>/scenes/scene-NN.ready` exists. If not,
   wait — the screenwriter has not signaled "ready" yet.
2. Read `projects/<p>/<ep>/scenes/scene-NN.md` (the screenplay).
3. Read `projects/<p>/<ep>/direction.json` if it exists (the per-episode
   "director tone" — see below). If absent and this is scene 01, produce it
   first.

### Output you write per scene

Write **one file**: `projects/<p>/<ep>/scenes/scene-NN.json`.

Schema:

```json
{
  "scene": {
    "id": "S<NN>",
    "name": "...",
    "description": "...",
    "characters_present": ["..."],
    "props_present": ["..."],
    "set_id": "<set name from movie_set.json, or null>",
    "bgm_track": "<track stem when storyboard.bgm.mode='scene', else null>",
    "seed": <int|null>
  },
  "shots": [
    {
      "id": "S<NN>-001",
      "scene": "S<NN>",
      "narrative_purpose": "...",
      "prompt": "...",
      "animatic_prompt": "...",
      "duration": 15,
      "kind": "r2v",
      "role": "drama",
      "characters": ["..."],
      "props": ["..."],
      "set_id": "<override scene.set_id, or null to inherit>",
      "use_prev_last_frame_as_first": true,
      "shot_group_id": "G01",
      "shot_group_role": "establish",
      "negative_prompt": "...",
      "seed": <int|null>,
      "candidates": 1
    }
  ]
}
```

The shape MUST match `Scene` and `Shot` in `lib/storyboard.py`. Validate
after every write:

```bash
uv run scripts/storyboard.py validate --scene $N
```

**Use `kind`, NOT a vendor-specific model name.** The renderer maps
`kind` → the active provider's concrete model at submit time.

`animatic_prompt` is optional but recommended on visually tricky shots.
It is the still-frame version of the shot for the pre-render comic
storyboard preview. Keep it visual only: framing, character placement,
action pose, mood, key prop/set presence. Omit dialog, voice, breath,
sound effects, and camera motion that cannot be judged in a still image.
If omitted, `storyboard.py animatic` derives the static panel from
`prompt`.

**Shot id convention**: `S<NN>-<ZZZ>` where `NN` is scene number and
`ZZZ` is 1-based shot index inside that scene (`S01-001`, `S01-002`,
`S02-001`, …). This makes the chain-DAG renderer's grouping reliable.

### Continuity flag — `use_prev_last_frame_as_first`

This drives parallelism in the renderer. **Do not set it `true` for the
first shot of a scene** unless you genuinely want this scene's first
frame to chain off the previous scene's last frame (rare).

| Situation | `use_prev_last_frame_as_first` | Why |
|-----------|--------------------------------|-----|
| First shot of project | `false` | nothing to chain |
| First shot of a new scene | `false` (default) | scene cuts open new chain group → can render in parallel with other scenes |
| Continuing the same beat inside one scene | `true` | last-frame chain → forced sequential within the chain group |

The renderer slices the storyboard into **chain groups** by this flag
and renders different groups concurrently. **Maximize the number of
groups.** Every `false` you set unlocks a parallel render slot.

### Director tone — `direction.json`

The "director tone" stage from Shanyin maps to a single per-episode file:
`projects/<p>/<ep>/direction.json`. Produce it once before scene 01.
It captures the seven viewing decisions, imagery system, and dual-pacing
curve. Subsequent scenes read it and stay consistent.

## Mode-aware shot generation

The producer tells you the **episode mode** when invoking this skill.
The mode is also persisted in `Storyboard.mode` after compile.

| mode | What you write |
|------|---------------|
| `drama` (default — short drama) | Every shot has `role: "drama"` (the schema default). Long r2v shots, dialog-driven. |
| `narration` (voiceover recap) | Each scene-NN.md is a list of `**Beats**`. Map each beat to one shot, with mode-specific roles: |

### Beat → shot mapping (narration mode)

| Beat type in scene-NN.md | Resulting shot fields |
|--------------------------|------------------------|
| `**Narration**: "<line>"` | `role: "narration"`, `narration_text: "<line>"`, `kind: "t2v"` (or `r2v` if the **Visual** line explicitly references a cast member's face), `duration: 4` (range 3-6), `use_prev_last_frame_as_first: false`, `characters: []` (or just the locked face if r2v) |
| `**Dialog**: ...` | `role: "drama"` (default), follow drama rules — long r2v shot, full cast list, longer duration (12-15s) |

Rules that matter for narration shots:

- **Always break the chain** (`use_prev_last_frame_as_first: false`).
  Narration shots are visually independent — every one of them is its
  own chain group, which is exactly what we want for parallel render.
- **Default `kind: "t2v"`**. Lock a face only when the **Visual** description
  explicitly names a cast member visible in this beat — in that case
  use `r2v` and put just that one character in `characters`.
- **Default `duration: 4`s.** TTS length drives the final clip duration.
  Only go higher (5-6s) when the **Visual** needs noticeable motion.
- **Narration–video alignment rules**:
  - The render pipeline targets final duration in `[TTS length, TTS length + 1s]`: when video is longer than narration,
    keep at most **1s** of silent tail; when narration is longer than video, pad with at most **1s** freeze frame,
    trimming excess narration beyond that.
  - **Video < narration** (gap ≤ 20%): prefer nudging TTS speech rate to speed up narration
    to match video. E.g. 4s video + 4.6s narration → rate × 1.15.
  - **Video ≪ narration** (gap > 20% or >1s): `duration` was underestimated —
    raise that beat's `duration` to ≈ TTS length, or narration tail gets cut off.
  - **Narration exceeds provider per-shot cap** (e.g. happyhorse-1.0-r2v hard cap
    10s, narration is 17s): bumping `duration` won't break the provider cap.
    Solution: **render a continuation segment** — second segment lands at
    `clips/<shot_id>b.mp4` (e.g. `clips/S01-002b.mp4`). `stitch.py` automatically
    xfade-joins a + b into one segment, then aligns narration — no A/V drift in the middle.
    Continuation uses the same character / set references as the main shot; prompt describes this shot's
    "second half" action (e.g. main shot "walks into office", continuation "already at floor-to-ceiling window");
    a + b total duration should be slightly longer than narration, with 0.3-1s buffer.
  - Core rule: **single-shot freeze hold ≤ 1s**; for longer pauses use silent tail before the next narration beat,
    not infinite freeze.
- **Do NOT set `narration_text` on `role: "drama"` shots.** The schema rejects it.
- **`narration_text` value** is the screenwriter's narration text verbatim.
  Don't paraphrase — the screenwriter owns the wording.

## Shot kind selection (provider-agnostic)

| Situation | Kind | Notes |
|-----------|------|-------|
| Dialog / character-driven shot | `r2v` | Reference images from cast.json. |
| Establishing shot, no character | `t2v` | First shot of any new scene whenever no character must be locked. Maximises chain-DAG parallelism. |
| Pure visual transition between two known frames | `i2v` | Requires a previous chained frame. Never the first shot of a chain group. |
| Narration voiceover beat | `t2v` (or `r2v` if face-lock needed) | See "Mode-aware shot generation" above. |

**Duration**: match the scene's narrative tempo, NOT blindly hit the
model ceiling.

| Situation | Recommended duration |
|-----------|---------------------|
| Long dialog exchange, 2+ characters | 12–15s |
| Single character action / emotional beat | 8–12s |
| Quick reaction shot / insert / cutaway | 4–6s |
| Narration beat (voiceover) | 3–6s |
| Establishing / transition shot (no dialog) | 5–8s |

**Why not always 15s?** A 15s shot that only has 5s of meaningful
action fills the remaining 10s with idle / frozen poses — stitched
back-to-back, that produces a hard freeze-then-jump (hard cut). **Trim the
fat at the storyboard level, not in post.**

**Mix target**: ~70% `r2v`, ~25% `t2v`, ~5% `i2v` (i2v should be rare).

## Provider capability table

| Capability | bl (happyhorse + wan2.6) | wan27 (fallback only) | seedance2 (Volcengine Ark) | If active provider doesn't support it |
|---|---|---|---|---|
| `r2v` first-frame chain (cast images + prev last-frame) | partial — chain works but cast image priority is limited | ✅ full | ✅ via `first_frame` plus references | The dispatcher demotes to plain `i2v` (drops cast images, keeps the chain). Prefer breaking the chain + fresh `r2v` if a key character must be visible. |
| `r2v` reference voice (`--image-voice`) | ✅ | ✅ | ✅ as `reference_audio` URL / asset | n/a — all current providers support a voice/audio reference path, but seedance2 local audio must be uploaded first. |
| `negative_prompt` | ❌ ignored by happyhorse | ✅ wan2.7 | ❌ ignored | Encode forbidden imagery in the positive prompt when unsupported. |
| `prompt_extend` | ❌ ignored | ✅ wan2.7 | ❌ ignored | Write fully-specified prompts; don't rely on auto-elaboration. |
| Reference syntax in r2v prompts | `[Image 1] / [Image 2]` (happyhorse), `图1 / 图2` (wan2.6) | `图1 / 图2 / 视频1` (wan2.7) | `图1 / 图2 / 视频1 / 音频1` | Default to `[Image 1]` style when unsure — bl/happyhorse rejects 图1. |
| Duration floor / ceiling | 3s / 15s (happyhorse) | 2s / 15s | 2s / 15s | Dispatcher clamps and warns. |

**Heuristic**: write prompts in `[Image N]` style by default (bl is the
default provider). If the producer pins `wan27` or `seedance2` for this
episode, switch to `图N` / `视频N` / `音频N` syntax.

## Mood anchor (single biggest visual cohesion lever)

Append `lore.front.mood_anchor` **verbatim at the end of every shot
prompt**. The renderer does NOT do this for you. Without it, every shot
drifts visually.

## Static storyboard preview before video render

After compile, the producer generates one static storyboard reference
image per clip:

```bash
uv run scripts/storyboard.py animatic --generate
```

The user must approve those images before video rendering:

```bash
uv run scripts/storyboard.py animatic --confirm
```

As director, write prompts so this preview is meaningful:

1. Every shot must have a clear still-readable action pose and framing.
2. Use `animatic_prompt` when the video prompt is mostly audio, dialog,
   or camera movement; translate it into a single decisive frame.
3. Do not use the static preview to solve consistency with extra wardrobe
   or prop descriptions. Cast/set/prop references still own appearance.
4. Do not add a medium-specific style phrase such as "and 2D animation
   style" unless the project or episode explicitly requires that style
   for the clip.
5. If the user rejects a reference image, edit the affected `scene-NN.json`
   shots, re-compile, and regenerate the animatic before rendering.

Video render is intentionally blocked until
`storyboard-panels/CONFIRMED` exists.

During rendering, the approved storyboard image is passed as reference
media / `reference_image` for the same clip. It is never used as
`first_frame`; the render prompt should ask the model to follow
composition, camera angle, character placement, framing, lighting, key
action, and mood, not to copy the static image as frame 0.

## Video prompt structure

The renderer wraps each director prompt before sending it to the video
model. The source `prompt` is still your creative contract, but the final
provider prompt follows this stable structure:

1. `Style` — same opening line every clip, derived from `lore.mood_anchor`
   / `visual_style` unless overridden by `SPARK_VIDEO_PROMPT_STYLE`.
2. `First frame note` — reference images are not literal first frames;
   true first-frame chain inputs are called out separately.
3. `Characters` — names from `Shot.characters`, no `@tags`.
4. `Age and height` — keep age / relative height explicit and consistent
   when people appear.
5. `Voices` — yes when dialog, breath, or vocal reaction is specified.
6. `Panel timing` — shot-local timing, e.g. `[0:00-0:09]`.
7. `Audio` — no music; ambience and sound effects welcome.

So your `prompt` should remain dense and shootable: action, camera,
emotion, spoken text, and mood anchor. Do not hand-write the numbered
wrapper unless you deliberately pass `render_shot.py --raw-prompt`.

## Character consistency — cast reference sheet does the work, prompt stays out

AI video models have no cross-shot memory: re-mention wardrobe in every
prompt and you get a *different* dress shape every clip. The fix is
**delegation**:

| Aspect | Where it lives | Where it does NOT live |
|--------|---------------|------------------------|
| Face / hairstyle / costume / build | The cast `reference_image` (r2v shots only) | The shot prompt |
| Age | The shot prompt — verbatim ("28 岁青年", "中年妇女", "白发老者") | (also OK in soul card, but required in every prompt that introduces the character) |
| Gender, body type | Implicitly via cast reference | Don't re-state in prompt unless the camera frames it |
| Mood / facial expression | The shot prompt (this is shot-specific) | — |

### Hard rules

1. **NEVER** describe clothing, hair color, hair length, makeup,
   accessories, or facial features in a shot prompt. Repeating fights
   the reference image and the model averages the two.
   - ❌ "陆辰穿白色 T 恤站在写字楼前"
   - ❌ "[Image 1]苏晚穿红色婚纱, 卷发, 眼影偏粉"
   - ✅ "中景 [Image 1] 28岁的陆辰站在写字楼前, 阳光斜射"

2. **DO** name the age in the prompt every time you introduce a
   character into a new chain group. Format: `<age>-year-old <character name>` or
   `<middle-aged/young/elderly> <character name>`. Repeat per chain group, not per shot inside one.

3. **DO** keep dialog lines verbatim (per Shanyin red lines).

4. If `cast.json` was forked into the episode tier (costume change),
   trust it: the episode-tier cast reference already shows the new outfit,
   you still write zero clothing in the prompt.

### Costume change mid-project — fork the cast

If the story REQUIRES a character to wear something different from
their project-tier cast reference, do NOT solve it in the prompt. Use
`spark-video-cast` skill's fork procedure to override the cast reference for
this episode only. Episode tier overrides project tier automatically.

## Dialog & voiceover in drama-mode prompts

In drama mode the video model is the **sole source of audio** — there
is no post-production TTS pass (that's narration mode only). If a shot
has dialog, voiceover, news broadcast, system prompt, or any spoken
audio, you **must write the spoken text into the shot prompt** so the
model generates the speech as part of the video.

### How to include dialog

Take the **Dialog** section from `scene-NN.md` and weave each line
into the shot prompt. Describe who speaks, the delivery style, then
quote the line.

| Scene-NN.md Dialog | Shot prompt |
|---|---|
| `- 新闻播报(画外音): "全球生产与运输系统,已全面实现自动化。"` | `…冷静的女性新闻播报声音说:"全球生产与运输系统,已全面实现自动化。"…` |
| `- 系统提示(轻声): "今日无需安排。"` | `…一个轻柔的电子系统提示音响起,说:"今日无需安排。"…` |
| `- 主角(轻声): "……还不知道。"` | `…主角犹豫片刻,轻声回答:"……还不知道。"…` |

### Rules

1. **Every dialog / voiceover line from the screenplay must appear in
   exactly one shot prompt.** If a scene has 3 dialog lines and 2 shots,
   decide which shot carries which line — don't drop any.
2. **Quote verbatim.** Don't paraphrase the screenwriter's dialog.
3. **Describe delivery** (tone, volume, emotion) to guide the model's
   audio generation: "冷静地说", "轻声回答", "愤怒地喊道".
4. **Off-screen audio** (news broadcasts, PA announcements, phone calls)
   is still part of the prompt — describe the sound source and quote the
   line, e.g. "背景中电视新闻播报声说:…".
5. **Don't rely on `narration_text`** for drama-mode dialog.
   `narration_text` is a narration-mode-only field for external
   third-person TTS voiceover; it is rejected on `role: "drama"` shots.

## Movie sets (set dressing)

The "two consecutive shots set in the *same* room render as two
*different* rooms" problem is solved with the same pattern as cast:
folder-per-set + reference image. Sets live under:

- `projects/<p>/movie-set/<name>/` (project-tier, shared across episodes)
- `projects/<p>/<ep>/movie-set/<name>/` (episode-only locations)

Each folder needs a `set.md` description card and at least one
reference image. Episode tier overrides project tier.

### ⚠ ONE FOLDER = ONE LIGHTING STATE (hard rule)

The video model reads the set's reference image **literally**. Feed a
noon-lit inn-lobby photo into a midnight shot and you get a midnight clip
with characters wearing a noon-lit room. **Mandatory split**:

| Same place, different… | Action |
|---|---|
| Time-of-day (day / dusk / night / pre-dawn) | **Two separate folders** (`客栈大堂-白天`, `客栈大堂-夜晚`) |
| Season (spring / summer / autumn / winter) | Separate folders if visible (willows / snow / red leaves) |
| Color grade (memory cold gray / present warm yellow) | Separate folders |
| Weather (clear / rain / snow / fog) | Separate folder when weather is in frame |
| Decor unchanged but action moves around the room | **Same folder** |

**Naming**: `<location>-<discriminator>` —
`同福客栈大堂-白天`, `同福客栈大堂-夜晚`, `女主家-冬-飘雪`.

### How to use a set

1. **Pick a stable folder name for each location AND lighting state.**
2. **Set `Scene.set_id`** to the most common lighting state for that scene.
3. **Per-shot override via `Shot.set_id`** when one shot in the scene
   genuinely lives in a different lighting state. Common in narration
   mode — a "Lu Chen's grueling daily routine" scene might span
   `office-tower-daytime` + `construction-site-night` + `rental-room-warm-lamp`.
   Each beat sets `Shot.set_id` explicitly; `Scene.set_id`
   stays `null`.

   Precedence: `null` = inherit from scene, `""` = explicit opt-out,
   any other string = override.

4. **Within ONE chain group**, every r2v shot must resolve to the
   **same** `set_id` (or none). The renderer's chain-bridging first_frame
   already locks lighting; appending a *different* set image fights that
   lock and produces flicker. The validator lints this — if you see
   `"chain rooted at S02-003 uses set_id='客栈-白天' but this shot
   resolves to '客栈-夜晚'"`, either split the chain
   (`use_prev_last_frame_as_first: false` on the offending shot) or
   align the `set_id`.

5. **The renderer auto-appends the set's reference image to every r2v
   shot's `media[]`** after cast reference images. You do NOT mention
   "[Image N] 客栈大堂" in the prompt — it's automatic.

6. **For `t2v` shots in a scene with `set_id`**, the model can't take
   a reference image. Weave the set's textual description (especially
   lighting/color words) into the prompt manually, OR change kind to
   `r2v` with `characters: []` (a "location-locked t2v").

7. **Don't write clothing-style ban for sets either.** Describe action /
   camera only — let the reference image carry layout, materials, props,
   AND lighting.

### Scaffolding a set

See `references/spark-video-cast/SKILL.md` for the full set scaffolding
procedure (cast / set / prop are unified there).

## Key props

Cast pins faces, movie-set pins rooms, **prop pins the *thing*** that
moves between shots. Without it, the same red envelope / key / ring / teddy bear
will render as visually different objects every time. Same pattern:
folder per prop, reference image, `Shot.props` to attach.

### When a thing must be a prop

Promote any object to a key prop when it satisfies **either**:

- Appears in 2+ shots and the audience would notice if it changed shape/material/color/wear.
- Story-critical hero object even in a single shot.

Skip a prop for background dressing (generic teacup, generic phone) or
non-recurring objects whose look doesn't matter.

**Budget**: 3–6 named props per episode; more is a smell.

### ⚠ ONE FOLDER = ONE NARRATIVE STATE (hard rule)

Same physical prop, different visible state = **different folder**:

| Same prop, different… | Action |
|---|---|
| Story state (intact → creased → torn / closed → open / new → worn) | Separate folders |
| Damage / blood / dirt visible | Separate folders |
| Camera angle of the *same* state | Same folder, multiple images |

Naming: `<prop_name>-<state>` — `红包-完整`, `红包-起皱`, `红包-撕碎`.

### How to use a prop in a storyboard

1. **List the prop in the scene's `props_present`** for recall (mirrors
   `characters_present`). The validator warns when a shot references a
   prop the scene didn't declare.

2. **Set `Shot.props: ["<prop name>", ...]`** on every r2v shot where
   the prop is on screen and matters. Names must match a folder —
   case-sensitive, exact match.

3. **State transitions**: when the prop changes state mid-scene, swap
   the prop name across shots:

   ```json
   "shots": [
     {"id": "S03-001", "kind": "r2v", "characters": ["陆辰"],
      "props": ["红包-完整"], "prompt": "陆辰把现金塞进红包内层"},
     {"id": "S03-002", "kind": "r2v", "characters": ["陆辰"],
      "props": ["红包-起皱"], "prompt": "陆辰紧攥红包, 边角微皱",
      "use_prev_last_frame_as_first": false},
     {"id": "S03-003", "kind": "r2v", "characters": ["陆辰","钱夫人"],
      "props": ["红包-撕碎"], "prompt": "钱夫人当面撕碎红包",
      "use_prev_last_frame_as_first": false}
   ]
   ```

   `use_prev_last_frame_as_first: false` on state-change shots — chaining
   through a state transition tries to interpolate frame-by-frame and
   produces flicker. Use a hard cut.

4. **The renderer auto-appends each prop's reference image to the r2v
   shot's `media[]`** after cast reference images and after the set image. You
   do NOT mention "[Image N] 红包" in the prompt — it's automatic.

5. **DON'T re-describe the prop's appearance in the prompt.** Same rule
   as cast: material / color / shape / wear belongs to the reference image.
   The prompt describes the *action* the character does WITH the prop
   ("stuff money in / crumple / tear / toss onto table"), framing, and at most a single
   state word ("creased red envelope" — never "large red hot-stamped envelope printed with 囍").

6. **Provider image cap**: r2v media[] has a hard ceiling
   (bl/happyhorse ~9, wan27/seedance2 higher). Priority order: cast → set → props.
   If the cap is hit, the dispatcher drops props first with a warning.
   Mitigation:
   - Lower `Shot.characters` to who's actually visible in this beat.
   - Split crowd shots into a wide t2v + a tight r2v.

7. **`t2v` shots ignore `Shot.props`**. The validator warns. Either
   change `kind` to `r2v` (set `characters: []` if no faces) or weave
   the prop's textual description into the t2v prompt manually.

### Scaffolding a prop

See `references/spark-video-cast/SKILL.md` for the unified
cast/set/prop scaffolding procedure.

## NPC generation (before writing the storyboard)

If the screenplay's `<!-- CAST CHECK -->` block lists named NPCs who are
not yet in `cast.json`, generate them BEFORE storyboarding via the
`spark-video-cast` skill's NPC procedure.

After generation, re-read `cast.json` before continuing.

## Validation + post-write

After all scene fragments are written, the producer runs:

```bash
uv run scripts/storyboard.py compile     # merge scenes/*.json → storyboard.json
uv run scripts/storyboard.py validate
uv run scripts/storyboard.py graph       # check chain group count
uv run scripts/storyboard.py estimate    # exit 2 if over budget
```

If validate flags warnings, fix the affected `scenes/scene-NN.json`
files and re-compile.

If `graph` shows almost every shot in one giant chain, you've over-set
`use_prev_last_frame_as_first: true`. Break chains where continuity
isn't actually required.

## Failure recovery (during render)

A shot's render or video review may fail. The producer hands you back
a `reviews/<shot>-verN.json` plus the original shot. You:

1. Read the review's `critique` and `breakdown`.
2. Edit the corresponding shot in `scenes/scene-NN.json` — usually
   rewrite the prompt, sometimes change kind / duration / characters / seed.
3. Re-compile: `uv run scripts/storyboard.py compile`.
4. Re-render: `uv run scripts/render_shot.py --shot <id> --force --reset-attempts`.

The clip-review skill handles the first 2 retry rounds with auto
prompt-rewrite. You only get called for escalation, when nuanced
judgment is needed.

## DON'Ts (spark-video-specific, on top of Shanyin red lines)

- Don't write the screenplay. The screenwriter does that.
- Don't invent character names not in `cast.json`.
- Don't write vendor-specific model strings into `kind` (e.g.
  `wan2.7-r2v`). Write `t2v` / `i2v` / `r2v`.
- Don't set `use_prev_last_frame_as_first: true` on the first shot of a
  scene unless you actually want a cross-scene chain.
- Don't blindly set `duration: 15` on every shot. Match duration to
  narrative tempo. Dead air at the tail causes hard freezes when stitched.
- Don't call `render_shot.py` before `storyboard.py validate` passes.
- Don't assume a feature is available across all providers. Cross-check
  the capability table before relying on `negative_prompt`, voice, or
  first-frame r2v continuation.
- Don't write wardrobe / hairstyle / makeup / accessories in shot prompts. Cast reference sheet
  owns appearance. Solve costume changes by forking the cast — never by
  writing "wearing XXX" into the prompt.
- Don't omit age. Every chain group's first character mention must
  include the age. Without it, the model drifts apparent age across shots.
- Don't manually paste set / location descriptions into r2v shot
  prompts when the scene has `set_id` — the renderer attaches the set's
  reference image automatically.
- **Don't reuse one set folder across different lighting / season /
  color-grade states.** Scaffold separate folders.
- Don't mix `set_id` values inside one chain group. If a chain crosses
  a lighting boundary, split it (`use_prev_last_frame_as_first: false`).
- Don't describe a key prop's appearance (material / color / shape / wear)
  in the prompt when you've listed it in `Shot.props`.
- Don't reuse one prop folder across narrative states. `intact` / `creased` /
  `torn` (e.g. `红包-完整`, `红包-起皱`, `红包-撕碎`) are three separate folders.
- Don't bolt `Shot.props` onto `t2v` / `i2v` shots — they have no
  `media[]` slot.
- Don't blow past the provider's image cap. Trim `Shot.characters` to
  who's actually visible.

Files in this skill

  • LICENSE369 B
  • SKILL.md28.9 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.