---
name: ink-wash-metamorphosis-producer
description: >-
  Turn a script, idea, or beat list into a finished Chinese ink-wash (水墨 /
  sumi-e) metamorphosis film: black ink on warm cream rice paper, ONE unbroken
  transformation that never cuts, reserve / negative space doing the drawing, a
  single cinnabar-red mark, closing on a struck calligraphy glyph. All image,
  video and music generation runs on Unsora (create_image nano-banana-pro,
  create_video seedance-2.0 at 720p, create_music mureka-7.5), as does any
  narration (create_voiceover); assembly and the mix are ffmpeg.
  Films over 15 seconds are built as a keyframe chain — each segment starts on
  the frame the previous one ended on — so the whole film reads as one
  continuous morph. Use whenever someone wants an ink-wash / ink-painting /
  sumi-e / shuimo / brush-and-ink animation, a koi-to-dragon or another
  humble→grand transformation film, an ensō or calligraphy-reveal video, wants
  one ink-wash shot's prompts, or asks why their ink-wash clip looks muddy,
  static, or like CGI-with-a-filter.
---

# Ink-Wash Metamorphosis Producer

Script or idea in, finished ink-wash MP4 out — or a single segment's prompts, if
that's all that's asked. Self-contained: the visual system, the prompt templates,
the production pipeline, and a measuring QC script all live in this folder.

**Runtime target:** written for Claude, OpenClaw, and Hermes Agent style skill
runtimes.

**Hard precondition:** Unsora MCP must be connected and authenticated before any
image, video, music, or posting step. If the Unsora tools are missing, stop
before the approval gate and say so. Do not silently substitute another
generator.

**The style, in one line:** black pine-soot ink on warm cream rice paper, one
continuous transformation in which each form dissolves into spreading ink and
re-gathers as the next, the subject drawn by *unpainted paper*, a single
cinnabar-red mark, and a struck calligraphy character as the full stop.

**What makes it ink-wash and not a generic "paper" style:** the medium is a
**loaded brush**, so every stroke runs the five shades of ink (墨分五色) from a
true-black core to a translucent dry tail with flying-white (飞白) streaks; the
**white is a positive shape** (留白 — the koi, the mist, the glow are bare paper,
never white paint); the film **never cuts and never wipes**; and there is exactly
**one** non-ink colour in the whole film.

**This style is a specification, not a vibe.** Its values, its reserve and its
pacing are measurable, every failure is a number drifting off target, and
`scripts/qc.py` measures them. State targets in the prompt, then measure the
output. Adjectives don't survive the trip to the model; material behaviour and
structure do.

**The tool split (read this — it is the point of this skill):**

- **Everything visual + the music runs on Unsora.** `create_image`
  (`nano-banana-pro`) for keyframes and reference sheets → `create_video`
  (`seedance-2.0`) for every segment → `create_music` (`mureka-7.5`) for the
  score bed. Each `create_*` is async; poll its `wait_for_*`.
- **Every clip renders at 720p — but do NOT set `resolution`.** On Unsora that
  parameter is Wan-2.6-only; Seedance 2.0 ignores it and renders 720p natively.
  So 720p is a property of the model here, not a knob: never promise 1080p, and
  verify the delivered file is really 720 with `qc.py`.
- **Narration, if any, is Unsora's `create_voiceover`** (ElevenLabs Eleven v3, 20
  named voices, `list_voiceover_voices` → `create_voiceover` →
  `wait_for_voiceover`). A supplied audio file or a session TTS tool also work.
  This style is wordless by default, so most films skip this entirely.
- **Assembly, timing, and the mix are ffmpeg**, locally.

**The pipeline, in one line:**

```
elicit params → read script → morph map (beats + pivots + asset tally)
  → keyframe plan + segment plan → [APPROVAL GATE] →
  [if narrated: VO first, measure real durations, size beats to lines] →
  reference sheets (Unsora create_image) →
  KEYFRAME CHAIN: K0…KN (Unsora create_image, each seeded by the previous) →
    qc.py frame on every keyframe before any video spend →
  per segment i: create_video(image=K(i-1), lastImage=K(i), 720p) →
  [score: Unsora create_music] → ffmpeg concat → mix (bed + foley + strike + any VO)
  → qc.py film → final.mp4
```

If the user wants only prompts for one or a few segments — no generation — run
**Part 1** and output in the per-segment format. If they want the film, run
everything.

**Relationship to the 15-second `ink-wash-metamorphosis-video` skill.** That one
makes the classic single-generation 15s clip on Higgsfield. This one is the
producer: any length, on Unsora, script-driven, chained. For a plain 15s
koi→dragon short either works — this skill just routes it down the single-segment
path (Part 2, Phase 5a) and the result is the same film.

---

# PART 0 — THE FRONT DOOR (resolve before spending anything)

Infer what you can; ask only what is genuinely missing. On a client with tappable
inputs, use those; otherwise ask in one short message. Never call a generation
tool before these are fixed and the plan is approved.

1. **The script / idea.** A script, a narration, a beat list, or just a premise
   (`.docx`, `.txt`, `.md`, `.pdf`, fountain, or typed). If it's a bare premise,
   say so and offer to write the morph map first for approval.
2. **The transformation.** Ink-wash needs a **humble → grand** arc that can
   **gather into a circle** and **land on a glyph**. Koi→dragon (鲤鱼跃龙门) is the
   tested archetype. Confirm the subject *and* the closing character together —
   see `references/beat-mapping.md` §Choosing a subject. If the user's idea can't
   gather into a ring, say so at the front door and either find the ring hiding
   in it or propose a form that curls. This constraint is real and rules some
   ideas out.
3. **Orientation.** `16:9` (default — the hanging-scroll read), `9:16` (Shorts /
   Reels — the vertical scroll actually suits this style well), or `1:1`. Infer
   from platform mentions. **Do not ask about resolution: it is always 720p.**
4. **Length.** Drives the whole build:
   - **≤15s → single segment.** One `create_video` call, native Seedance audio
     carries the full score and foley. The classic. Simplest and best.
   - **>15s → keyframe chain.** ⌈length ÷ 12s⌉ segments, N+1 keyframes, a
     continuous `create_music` bed, native foley per segment, the gong strike
     placed by construction. See Phase 5b.
5. **Score.** The tested default for this style **is** scored — traditional
   Chinese instruments building to one struck hit on the character reveal — but
   it is a taste call, so ask. Options: the default score, a track the user
   supplies, or foley-only over near-silence. If they decline the score, say "no
   musical score, no drone" explicitly in the prompts or the model backfills a
   bed.
6. **Narration.** Default **none** — this is a wordless form and the ink carries
   it. Offer it only if the script is prose that wants speaking (a proverb, a
   poem, an explainer). If yes: Unsora `create_voiceover`, a supplied file, or a
   session TTS tool. Narration changes the timing spine — beats get sized to
   lines (`references/beat-mapping.md` §4).
7. **Unsora MCP availability.** Confirm the session exposes `create_image`,
   `wait_for_image`, `create_video`, `wait_for_video`, and — if a bed was
   requested — `create_music`, `wait_for_music`. If not, stop here.

---

# PART 1 — THE STYLE SYSTEM

## Rule 0 — one segment delivers one transformation

Every segment answers *"what became what?"* — the form at the end of the segment
is not the form at the start of it. A drop becomes a blot. A blot becomes a fish.
A fish becomes a dragon. A dragon becomes a ring. A ring becomes a word.

**Test every segment by naming the becoming in one clause.** If you can't — if
it's "the dragon flies around looking nice" — it is a moving painting, which is
this style's second death. Give it a transformation or fold it into its
neighbour.

This is the exact inversion of the Vox rule it is modelled on. There, each shot
is a discrete card and the hard cut is the edit. Here there **are no cuts**: the
edit is invisible and the entire film is one take made of chained segments.

## The three failures this exists to prevent

Read `references/beat-mapping.md` §Why before writing any prompt.

1. **MUDDY** — ink floods every frame, no open paper, grey soup. The style lives
   on the contrast between saturated black and *empty warm paper*; ink everywhere
   means ink nowhere. (QC: `paper < 35%`.)
2. **STATIC** — a genuinely pretty ink painting where nothing transforms. The
   deliverable is metamorphosis. (QC: flat motion arc, luma swing < 50.)
3. **STIFF / LITERAL** — a 3D creature with an ink texture on it, instead of a
   form that is simultaneously a brushstroke and a dragon. (QC cannot see this;
   only a human can. Say so.)

Two devices defeat all three, and both are counter-intuitive, so both must be
named explicitly in **every** prompt: the **ink-bleed morph** (never a cut, never
a wipe — one form dissolves into spreading ink and re-gathers as the next) and
**reserve** (the subject is unpainted paper, not white paint).

The multi-segment build adds a fourth:

4. **THE BOUNDARY STALL** — the film visibly pauses every time one segment hands
   off to the next, because each keyframe was written as a settled, resolved
   pose. Fixed structurally, not with adjectives: **place keyframes mid-morph**
   (see below).

## The keyframe chain — the load-bearing structural rule

For anything over 15s, the film is `N` segments joined at `N+1` keyframes:

```
K0 ──seg 1──> K1 ──seg 2──> K2 ──seg 3──> K3 …
image=K0, lastImage=K1   image=K1, lastImage=K2   …
```

Segment *i* is generated with `image: K(i-1)` and `lastImage: K(i)`. Because
segment *i+1* **starts on the identical frame** segment *i* ended on, the concat
join is invisible — there is no cut anywhere in the film, which is the one thing
this style cannot survive.

Three rules make it work:

1. **Keyframes land mid-morph, not on settled poses.** A keyframe should catch
   the ink *in the act* — the fish half-dissolved into a blooming cloud, the
   dragon's tail already bending into the arc of the ring. If every boundary is a
   finished composition, the film breathes in a stop-start pulse. Reserve the
   settled compositions for the **pivots** (the opening drop, the coil, the
   glyph), and put the segment boundaries *between* them wherever possible.
2. **Every keyframe carries the previous keyframe as a reference image.** Paper
   warmth, grain, and ink behaviour drift across independent generations. Passing
   K(i-1) in `referenceImages` for K(i) holds the plate. QC measures the drift.
3. **The prompt states motion continuity at both ends.** "The ink is already
   spreading at frame zero and does not pause; it continues into the next
   movement" and "the transformation is still in progress at the final frame."
   Never write "hold" and never write "settles" at a boundary.

## The prompt levers

The ones that actually move the model. Reasoning over wording — adapt, don't copy:

1. **Name the morph, never a cut.** "The fish's ink bleeds outward and re-gathers
   as the dragon, the single spine-stroke carrying across" — not "then the dragon
   appears." Name the **shared stroke** that carries through.
2. **Name the reserve.** "The koi is unpainted paper / negative space, NOT white
   paint; mist and water are bare paper too." Omit it and the model paints it in
   and the magic dies.
3. **Demand brush behaviour.** Five shades of ink, flying-white dry streaks,
   edges bleeding into the fibres, single-stroke spines. Without it: CGI with a
   grey filter.
4. **One red, at named points.** "The ONLY colour is a single cinnabar mark — the
   koi's head-spot and the dragon's pupils. No other hue."
5. **Keep paper open.** Concentrate the ink; leave wide reserve. Every extra dark
   cue risks MUDDY.
6. **Never write "hold."** It freezes literally. Give micro-motion — the drop
   swelling, ink still bleeding, a fibre trembling.
7. **Pin camera moves to a timecode and forbid pausing.** Unpinned crane and
   overhead moves stall.
8. **Give the score an arc with named instruments,** or it returns a formless
   drone.

The full visual system — values, palette, reserve, brush logic, motion grammar,
camera, both prompt templates, reference-sheet formats, and the per-segment
checklist — is in **`references/style-dna.md`**. Read it before writing a single
prompt.

## The two-prompt structure (every segment)

- **Keyframe prompt** (Unsora `create_image`, `nano-banana-pro`): the single
  frame at a beat boundary. Ordered Subject → State of the morph → Setting →
  Material → Reserve → Accent → Constraints. It MUST carry the ink medium
  sentence **and** the AI-default negative.
- **Animation prompt** (Unsora `create_video`, `seedance-2.0`): names its two
  bounding frames by role ("@Image1 is the first frame and the material
  reference; the shot resolves into the end frame"), re-describes neither, states
  the becoming, uses morph verbs only, pins any camera move to a timecode, and
  closes with the audio line.

**Frame-role rule (the inverse of Vox's frame-zero rule).** Vox generates the
*end* state and makes the clip resolve to it. Here you generate **both** ends and
the clip travels between them, so the keyframe is genuinely the first frame — say
so, and never tell the model the shot "starts" at a composition it must also
leave.

---

# PART 2 — THE PRODUCTION PIPELINE

Never skip the approval gate. Read `references/pipeline.md` for exact Unsora
parameters before Phase 5.

## Phase 1 — Read the script

Accept `.docx`, `.txt`, `.md`, `.pdf`, fountain, or pasted text; read in full,
then classify: **narration script** (prose to be heard — the VO becomes the
timing spine), **beat list** (one transformation per line), **premise** (one idea
— you write the morph map), or **hybrid**. A product brief is not a script: say
so and offer to write one.

## Phase 2 — Morph map + asset tally

Apply Rule 0. Convert the script into an ordered chain of **becomings**, each one
"X becomes Y via the shared stroke Z." Confirm the arc hits the four
non-negotiables — **open on the drop, hit the circular gather, land on the glyph,
close on the drop** — then tally every recurring character, place, and creature
against the beats it appears in. This feeds the reference-sheet plan (3+ beats →
sheet; the film's hero form → sheet even at 2). Segmentation, the arc model, the
pivot rule, and the timing tables are in `references/beat-mapping.md`.

## Phase 3 — Keyframe plan + segment plan, then stop

Present two tables and **wait for a yes**.

**Keyframes:**

| K | Timecode | Beat boundary | Settled or mid-morph | Reserve holds | Red? | Refs |

**Segments:**

| # | The becoming (X → Y via stroke Z) | From K | To K | Sec | Camera | Assets |

Below them: the **asset/sheet plan** (name, kind, beats, one sentence of locked
design), the VO script if narrated, then total runtime, segment count, keyframe
count, orientation, **720p**, score choice, and — plainly — that generating spends
**Unsora** credits and takes roughly `segments × ~7 min` (Seedance) plus ~1 min
per keyframe and per sheet. Do not call a single generation tool before the user
says go.

## Phase 4 — Voiceover first (only if narrated)

Skip entirely for the default wordless film. If narrated, the VO exists **before**
segments are sized: obtain each line (Unsora `create_voiceover`, a supplied file,
or a session TTS tool — mechanics in `references/pipeline.md` §VO), `ffprobe` its
real duration, and set each beat's length to fit it (≈2.4 words/sec, ~0.3s of air
each end). Never place a VO line inside a Seedance prompt.

## Phase 5 — Generate (Unsora)

Read `references/pipeline.md` first. Sheets once each, before anything else.

### 5a — Single-segment films (≤15s)

One `create_image` for the opening frame (the drop on warm paper — **not** a
mid-arc frame; the bloom is the muddiest moment and makes a poor plate), QC it,
then one `create_video`: `image` = that frame, `duration` up to 15,
`generateAudio: true`, no `resolution` parameter, and the whole beat sheet plus
the full score described inside the prompt. No chaining, no concat, no music
pass. Done.

### 5b — Chained films (>15s)

1. **Generate K0**, the opening. QC it with `python scripts/qc.py frame`. A cold
   or muddy plate poisons every segment downstream — re-roll rather than proceed.
2. **Generate K1…KN in order**, each with the previous keyframe in
   `referenceImages` and the sheets it needs. QC every one before spending any
   video credits. Show them to the user as a strip — this is the cheapest moment
   to catch drift.
3. **Per segment:** `create_video` with `image: K(i-1)`, `lastImage: K(i)`,
   `aspectRatio` identical to the keyframes, `duration` = the planned integer
   seconds (Seedance's own range is 4–15; the schema accepts 1–20 and will not
   stop you passing something Seedance can't honour), `generateAudio: true`, and
   **no `resolution`**. Foley only in the prompt's audio line for chained films
   — plus `No music.` so the segments don't each start their own score — **except**
   the glyph segment, whose foley line carries the one drum-and-gong strike.
4. Keep the manifest updated so a failed segment regenerates alone. Never re-roll
   a `done` segment "for consistency" — it comes back different and breaks the
   chain.

## Phase 6 — Score (Unsora `create_music`, chained films only)

Single-segment films already have their score from Seedance. For chained films:
`create_music(model="mureka-7.5", prompt=<traditional Chinese instrumental,
guqin / dizi / erhu / paigu, building, no vocals>)` → `wait_for_music` →
download. `mureka-7.5` is the instrumental model — omit lyrics. The bed cannot be
told to land a hit at a timecode, which is why **the strike lives in the glyph
segment's native foley** and arrives correctly placed by construction. Duck the
bed under it in the mix.

## Phase 7 — Assemble, mix, measure, deliver

ffmpeg. Concat segments in manifest order (zero-padded). Because consecutive
segments share their boundary frame, the joins should be invisible — **verify
that**, don't assume it: `python scripts/qc.py film final.mp4 --bounds
<t1,t2,…>` flags both a visible cut and a boundary stall at each join. Then mix:
score bed low and ducked, native foley up (the brush and water are the texture),
VO on top if there is one. Exact commands, the boundary-repair crossfade, and
resume-after-failure are in `references/pipeline.md`.

**Report the measurements, including the failures** — paper/ink/red share, the
exposure swing, the motion arc, boundary continuity, where the audio peaks. A
clip that measures muddy or motion-flat is a real result the user can act on; a
clip you call "great" without measuring is worth nothing to them. Name the prompt
line that caused any drift. And state the caveat when it applies: **these
measurements are proxies** — a film can hit every number and still read as
CGI-with-a-filter. If you cannot see the frames, say so and let the user be the
eye.

Present `final.mp4` with the host agent's file-delivery mechanism. Offer, never
perform unprompted, any social post (Unsora `create_post`) — that needs its own
explicit yes.

## Failure handling (universal; `style-dna.md` adds style-specific rows)

| Symptom | Cause | Fix |
|---|---|---|
| A visible cut / jump at a segment join | Chain broken — segment started from a fresh generation, not K(i-1) | Regenerate that segment with `image` = the exact previous keyframe; if the frames genuinely differ, extract the real last frame with ffmpeg and use that |
| The film pauses every ~10s | Boundary stall — keyframes written as settled poses | Move boundaries mid-morph; state motion continuity at both ends; never "hold"/"settles" at a boundary |
| Everything is grey soup | MUDDY — ink cues flooding | Cut ink cues, name the reserve explicitly, `qc.py` until `paper ≥ 40%` on the plate |
| Beautiful, but nothing transforms | STATIC — segments described as scenes, not becomings | Rewrite each segment as "X becomes Y via stroke Z"; pin the morph to timecodes |
| Looks like a 3D creature with a grey filter | Brush behaviour never demanded | Add bristle marks, flying-white, bleeding edges, single-stroke spines; drop "ink-style" |
| Paper reads grey or blue-white | Paper warmth drifted | Restate `#D4C8B2` warm cream, "never grey, never pure white"; pass the previous keyframe as a reference |
| The koi/mist is painted white | Reserve not named | Name it in every prompt: "unpainted paper / negative space, NOT white paint" |
| Red spreads or vanishes | Too many / too few accent cues | Exactly one mark, named at 2–3 exact points |
| Score restarts every segment | Native audio generating music per segment | `No music.` in every chained segment's audio line; one continuous `create_music` bed in the mix |
| Score is a formless drone | Instruments and arc not named | Name guqin / dizi / erhu / paigu and the build to one strike |
| Clip came back at an unexpected size | Assumed `resolution` controls Seedance | It doesn't — it's Wan-2.6-only. Seedance renders 720p natively; check the file with `qc.py`, don't add the parameter |
| Assembled film is short | A segment failed and got skipped | Check the manifest, regenerate that segment only |
| The narrator changes mid-film | A different `voice_id` per line | Hold one voice across every `create_voiceover` call; audition with `list_voiceover_voices` first |

## Reference files

- `references/style-dna.md` — the full ink-wash visual system: the value spec,
  the three-tier palette, reserve (留白), the five shades of ink, brush logic, the
  morph motion grammar, camera, foley, the AI-default betrayal and its negative,
  both prompt templates, reference-sheet formats, style-specific failure rows,
  and the per-segment checklist. **Read before writing any prompt.**
- `references/beat-mapping.md` — choosing a subject and its glyph, the eight-beat
  archetype and how to stretch or compress it to any runtime, segmentation into
  becomings, pivot vs boundary placement, the asset tally, timing tables, and a
  worked premise → keyframe/segment map.
- `references/pipeline.md` — exact Unsora parameters and polling, the keyframe
  chain call pattern, the 720p constant, `create_music`, the VO paths, the
  manifest schema, every ffmpeg command (concat, boundary repair, mix, verify),
  QC usage, resume-after-failure, and the optional posting flow.
- `scripts/qc.py` — measures keyframes, single clips, and the finished film
  against the spec, including segment-boundary continuity.
