Day 2. April 16, 04:28–09:35. OpenRouter, Kling, and the MUAPI character_id

Topped up

04:28. The user topped up Evolink, sent two tasks: download the Seedance prompting guide verbatim (no paraphrasing), and refresh movie_pipeline.md with current data.

The first iteration failed — the agent started paraphrasing the guide into Russian. User:

"What are you doing, the prompting guide needs to be downloaded verbatim from the link, not paraphrased. Why the hell are you rewriting it in Russian? It's for your context, not for me to read, it needs to be 1:1."

Re-downloaded the original 1167 lines. Updated docs/movie_pipeline.md:
- Step 6 (video) rewritten: prompt format, @Image tags, timecodes, negatives, what works / what doesn't.
- Registry — Shot 2 (door with boot) + Shot 2a (clean door) with exact prompts and the mockup algorithm.
- Log: rembg, Evolink CDN cache, minimal prompts, censorship.

SSL on Evolink → OpenRouter Seedance

v5 (reinforced prompt, 1-sec door kick) — run died on SSLError EOF. Five attempts in a row — Evolink temporarily down. Switched to OpenRouter Seedance (also version 2.0, price roughly equal).

First run — 413 Request Entity Too Large: char_bandit.jpg = 6336×2688, heavy. Compressed to 1280px. Found the format: endpoint /api/v1/videos, refs through frame_images (first frame) + input_references (persona).

[missing] shot1_video_v5 — OpenRouter, 10s, $1.512
../archive/shot1_video_v5.mp4

Cost: $1.512 for 10s 720p. Evolink Standard ~$1.53, Fast ~$1.24.

"Video is pretty good, but one thing — the door suddenly grew a hole when it opens. I can do it in DaVinci with masks from two generations. Worth doing that or do we keep generating?"

The agent answered that DaVinci compositing is more correct than re-generation — 90% is already there, and a re-gen costs $1.50 with no guarantee, the artifact might leave or a new one might appear. In real film production nobody generates to perfection, composite is post-production territory.

"The bandit doesn't look like himself"

"What's the full prompt in this video? The bandit doesn't look like himself — I just zoomed in and saw."

The agent showed the v5 prompt. User:

"Are you fucking serious. Re-read the prompting guide — where are the explicit @image1 @image2 markers? What the hell. And of course you should give the front view from the sheet, not the sheet itself. Goddamn, you don't show me your prompts and then it turns out you've been doing crap."

Two mistakes:
1. frame_images and input_references are separate channels in OpenRouter API. @Image1/@Image2 tags map to position in one list, but the agent split them across two. The model saw @Image2 in text, but the actual char_bandit went as a "general reference" — the tag wasn't bound.
2. Fed the full 6336×2688 sheet → after compression to 1280, the face became tiny, the model couldn't extract identity.

Gemini vision bbox

The agent tried to crop the front view from the sheet manually — first grabbed a side profile, then cut off the legs.

"Christ, give me a sec, I'll open Photoshop and do it myself. Now you cut off the legs, blank space on the left. What's wrong with you? Isn't there some model or library that can mark up the image cleanly?"

The fix — Gemini vision via OpenRouter, one query "return coordinates of the front view figure":

bbox = {'x1': 46, 'y1': 53, 'x2': 218, 'y2': 918}  # scale 0-1000
scaled bbox: (291,142) → (1381,2467) = 1090×2325

Clean front view: full height, both arms, face large, gray background. ~1 cent. Auto-crop for any character sheet from MUAPI.

"Look, I did it myself, mine is better than your version, just saved as jpg."

char_bandit_front

char_bandit_front

char_bandit_face

char_bandit_face

"The prompt is shit"

"And the prompt is shit, vague. What's a SHARP violent kick, door explodes inward with crash. You have to write 'kicks the door, the door blows open'. That's why you got splinters and holes. 'Bandit from @Image2 appears in doorway, frozen' — what, is he supposed to come in covered in icicles? You describe everything in metaphors instead of concretely. Why?"

What was written What the model understood What was needed
door explodes inward door explodes → splinters, holes door swings open fast, hitting the wall
SHARP violent kick aggression, destruction a boot kicks the door from outside
frozen in place frozen, ice standing still, not moving
eyes fixed staring intently (OK) looking at the body

The agent writes like a screenwriter — metaphors, emotion. The model reads literally. Locking in: physical descriptions, not states or characteristics.

Back to Evolink. v6, v7

Ran on OpenRouter with three refs (scene + front + face), all in input_references in order. Response:

400 InputImageSensitiveContentDetected.PrivacyInformation
The request failed because the input image may contain real person

OpenRouter rejected the face as a real person. User:

"Why did you switch to OpenRouter? We tested it once, figured the price is the same and the input is uncomfortable."

Back to Evolink (SSL recovered). v6: 10s, 3.8 MB, three refs. Bandit reads.

[missing] shot1_video_v6 — Evolink, 3 refs
../archive/shot1_video_v6.mp4

"As I said — the door doesn't fly off the hinges, it opens softly. Think how to fix in the prompt."

Gave 2 seconds for the kick ([00:03-00:05]) — model stretched the opening. Compressed to 1 second + literal frame-by-frame physics.

"Wait, also another quirk: since the bandit ref is in color, it added some color to the bandit. Need to make the ref B&W."

Converted both refs to grayscale (overwrote the files directly — lost the originals, later had to ask the user to re-save).

v7content_policy_violation: "may violate third-party content rights". The B&W face ref ended up too similar to a real celebrity (possibly Dean Norris). Added light noise on the face — passed.

[missing] shot1_video_v7 — B&W refs + noise
../archive/shot1_video_v7.mp4

v8 — approved

"1. No, the door opens softly. Also no pause at the start where the legs just sway.
2. Looks like him. 3. Color is B&W."

The agent had stripped the energy along with the metaphors, and v7 lost the kick's punch. Decided to merge — energy words for physics (boot hits hard, flies open, slams with loud bang), explicit door state (closed/open), @Image tags preserved.

The user spotted a contradiction:

"'The hanging legs sway gently. Nothing moves' — what does Nothing mean? You just wrote that the legs sway. Maybe write explicitly that the door is closed?"

v8 — final merged-style:

[00:00-00:03] Heavy silence. Hanging legs on the left sway gently.
              The door on the right is closed. The room is still.
[00:03-00:04] The door is kicked open with force from outside,
              slamming against the wall with a loud bang.
[00:04-00:07] The man from @Image2 steps through. Hands at his sides,
              nothing in hands. Stops past the threshold.
[00:07-00:10] The man stands still, looking straight ahead at the hanging body.
              The legs still sway.
shot1_video_v8 — original before rename
params 720p · 10s · 21:9
prompt
Style: black and white 35mm film, high contrast, film grain.
Camera: static wide shot, no movement.

@Image1 as first frame and background.
@Image2 as character full body appearance reference.
@Image3 as character face reference.

[00:00-00:03] Shot 1 (static wide): Heavy silence. The hanging legs on the left sway gently. The door on the right is closed. The room is still.
[00:03-00:04] Shot 2 (static wide): The door is kicked open with force from outside, slamming against the wall with a loud bang.
[00:04-00:07] Shot 3 (static wide): The man from @Image2 steps in through the open doorway. Hands at his sides, nothing in hands. Stops past the threshold.
[00:07-00:10] Shot 4 (static wide): The man stands still, looking straight ahead at the hanging body. The legs still sway.

Avoid jitter. Avoid bent limbs. Avoid identity drift.
shot1_video_v8 → shot1_video_door_kick — APPROVED

"Got it, the door is closed for literally a second, but extending that in post is no problem. Everything else is good."

Renamed to shot1_video_door_kick.mp4. Final Seedance prompt formula: Style:/Duration:/Camera: prefix + explicit @Image roles + timecodes + literal physics + negatives.

Kling O3 experiment

"Now we're playing with other models. Starting with Kling. Here's the prompting guide: github.com/aedev-tools/kling-3-prompting-skill"

Downloaded the guide (210 lines). Found more on blog.fal.ai.

Kling's key feature — Custom Element: you create char_bandit_front.jpg once → get an element_id. Then <<<element_1>>> in the prompt — the model knows the character internally, doesn't guess from a ref.

"I'd switch to 16:9 in general (fix this everywhere, your defaults are 21:9)."

Changed defaults in seedream_gen.py, seedance_video.py, flux_gen.py to 16:9.

First two attempts to create an element — failed invalid_parameters. Reason: char_bandit_front.jpg was grayscale (the agent had overwritten it earlier). Kling requires color for face recognition.

Tried to re-crop the face from the color sheet via Gemini bbox — returned a forehead without a chin. Gemini bbox is reliable for large figures, not small face thumbnails.

"Your bbox isn't working, why?"

The user re-saved both from Photoshop in color. Element created in 15 seconds: element_id: 308282182539136.

Multi-shot — disaster

Kling accepts multi_shot: true + multi_prompt: [...] — native multi-shots with timing.

shot1_video_kling_v1 — multi_shot

"Main fuckup — Kling broke the video into 3 shots. Is there a way to avoid this in the guide?"

"Sorry, but if you had the guide, why didn't you read it? You didn't know we want one shot?"

The agent didn't re-read the guide before generating. It says explicitly:
- Multi-Shot = different camera angles with cuts (for storyboarding).
- One continuous shot = Image-to-Video with a single prompt and temporal progression (first → then → finally).
- Marker phrase at the end: "No scene cuts throughout, one continuous shot."

shot1_video_kling_v2 — without multi_shot

"The door isn't kicked at all, just gently opened by hand. Bandit is OK. Let's try fixes at 1080 already, everything else is mush."

1080p = 90 credits, 34 left. 720p = 54, also doesn't fit. 5 sec without sound = 27 credits — fits.

shot1_video_kling_v3 — 5s, boot smashes

"Better, but he opened with his hand, and overall quality is so-so. I think Kling just loses to Seedance overall."

Agent's response: Seedance 2.0 from ByteDance (TikTok) vs Kling 3.0 O3 from Kuaishou (Kwai). Kling is formally newer by a couple of months, but ByteDance has a huge data advantage — all of TikTok as training data.

Seedance is better on quality and physics, Kling — almost half the price and time (72 vs 135 credits, 164 vs 320 seconds). For fast iterations on mediocre quality — Kling, for finals — Seedance.

MUAPI character_id

"Character is for video specifically — there's a system. You generate a character from a picture, you can pass the id to the video gen."
"Maybe those character sheets I downloaded aren't just for direct use, they're more for verifying that I'm OK with what it came up with, and you can re-generate the character. And Kling doesn't tell you what it generated."

The agent missed a key thing:
1. The Seedance v8 bandit already looks similar — identity through front+face refs works.
2. seedance-2-character returns character_id, but in the pipeline we save it and don't pass it anywhere to video gen. Bug.
3. Sheets = verification for the human. The character_id goes into generation, not the pictures.

Syntax: @character:557fcc62-b59b-45e7-8965-1d6fae091889 in the prompt text.

Created a character for the bandit through MUAPI: char_bandit_front.jpg + char_bandit_face.jpg → character_id 557fcc62-b59b-45e7-8965-1d6fae091889 + sheet_url for verification.

"So now you don't need to send the bandit ref, maybe it works."

Smart. Without a face ref in images, OpenRouter won't kill on privacy. @character:ID goes only in text:

shot1_video_or_chartest — OR + @character:ID

"Random dude :)"

Meaning @character:ID works only via MUAPI directly, not through OpenRouter/Evolink. MUAPI has its own endpoints: seedance-v2.0-i2v, seedance-v2.0-t2v, seedance-2.0-omni-reference.

Attempts via MUAPI — upload to cdn.muapi.ai times out from RU, direct Evolink URL passthrough also failed, plus MUAPI has 0 credits.

"Can you check whether character_id even exists in the official Seedance, or is this a MUAPI invention?"

It's a MUAPI feature, not the official ByteDance API. Official Seedance 2.0 (BytePlus) has only T2V/I2V/Reference-to-Video with up to 9 image refs, but no persistent character identity. MUAPI built it on top.

"Accepting as the working result."

Reorganization: scripts/movie_experiment/

"Create a folder scripts/movie_experiment, move everything we've dumped at the root into there. Make a separate git repo there. Add it to gitignore in the main repo."

53 files moved, separate git repo, .gitignore updated.

"And where did the archive go?"

The archive was dumped into the root during the move. The user clarified the structure:

"Scripts in src, archive in archive, approved in approved, working folder for current work."

Final layout:

scripts/movie_experiment/
├── src/          # flux_gen.py, seedream_gen.py, seedance_video.py, banana_gen.py, wavespeed_gen.py
├── approved/     # m1_priton_v7_max.png, shot1_pinata_corpse.png, shot1_door_closed.png, shot1_door_with_foot.png, shot1_video_door_kick.mp4
├── archive/      # 34 files — old versions, tests
├── docs/         # movie_pipeline.md, seedance_prompting_guide.md, kling_prompting_guide.md
└── (root)        # current working files

Four commits: initial → docs → reorganize → cleanup.

Skill and "you liar"

"Can you install this on yourself and tell me if it's useful? github.com/nidhinjs/prompt-master. Don't actually install yet. 1) can you install 2) is it useful."

The agent answered "I can't, it's a Claude.ai Skill via the web interface."

"You liar. https://code.claude.com/docs/en/skills — it says right here that you put it in a folder in the project and it just works."

Indeed — .claude/skills/<name>/SKILL.md and Claude Code picks it up automatically. We didn't install — model-specific guides (Seedance, Kling) are more precise than a generic prompt optimizer.

Window shot

"Let's keep playing with the experiment, I need a shot of the legs toward the window in backlight. So the toes will be on the left — leg detail."

First attempt: Seedream 5.0-lite on Evolink with M1 as ref → legs not vertical, mid shot instead of detail:

shot_legs_backlight — Seedream + M1 ref

shot_legs_backlight — Seedream + M1 ref

Flux in parallel — legs literally on the windowsill:

shot_legs_backlight_flux — Flux, legs on the windowsill

shot_legs_backlight_flux — Flux, legs on the windowsill
params flux · flux.2-max · 16:9
prompt
Detail shot, close-up of hanging legs and shoes in silhouette. Camera looks toward a large window. Strong backlight from the window, bright overexposed light behind the legs, creating a dark silhouette of the legs and shoes against the bright window. Toes of the shoes point to the left. The legs hang motionless in mid-air. Old apartment with faded floral wallpaper visible. Black and white 35mm film, high contrast, deep blacks, visible grain. Only legs from mid-thigh to shoes visible, no face, no upper body.

Tried extreme close-up — Flux censored "dead man hanging", Seedream did it but boots stand on the floor:

shot_legs_v2_seedream — standing on the floor, not hanging

shot_legs_v2_seedream — standing on the floor, not hanging

Plus Flux separately without M1 — just a shot toward the window:

m2_window_backlight — first attempt Flux without M1-ref

m2_window_backlight — first attempt Flux without M1-ref
params flux · flux.2-max · 16:9
prompt
Extreme close-up detail shot inside an old Stalinka apartment. Camera at low angle, looking toward a large bright window. Strong backlight from the window, overexposed white light flooding through dirty lace curtains. Faded floral wallpaper on both sides. Old herringbone parquet floor in the bottom of the frame. The window fills most of the background with bright blown-out light. Dust particles visible in the light beams. Empty room, no people, no objects in the foreground. Black and white 35mm film, high contrast, deep blacks, visible grain.

"No, this is total crap, this is 100% not the same room, just some shot toward the window in the same style. We have 2 windows actually. Do you remember the prompt and model that worked for the door shot?"

Right — for the door we used Seedream 5.0-lite + M1 as ref with a minimal prompt. Same approach for the window.

"Wait, why didn't you give Flux the M1?"

Brain fart. Flux also supports --ref. With M1 → m2_window_flux.png:

m2_window_flux — Flux with M1 as ref

m2_window_flux — Flux with M1 as ref

"Flux is fine, but the shot is too wide, it grabbed two windows. Can Flux improve quality? I have an idea about doing angles using Marble."

Marble — World Labs (Fei-Fei Li). One picture → 3D Gaussian Splatting world → free camera navigation → export angle. Idea: M1 → Marble → geometrically correct frame (mushy) → img2img through Seedream → detail with composition preserved.

m2_window_tight — one window, Flux with M1

m2_window_tight — one window, Flux with M1
params flux · flux.2-max · 16:9
prompt
Same room as in the reference image. Tight close-up framing on one window only (the left window of the two). Camera low angle looking up toward the window. Strong backlight, bright overexposed light flooding through the dirty lace curtain. The same faded floral wallpaper visible on both sides of the window frame. Herringbone parquet floor at the bottom edge. Dust particles in the light beams. Empty room, no people. Same black and white cinematic style as reference.

"It ate the windowsill and the window is too low to the floor."

m2_window_tight_v2 — windowsill+radiator

m2_window_tight_v2 — windowsill+radiator
params flux · flux.2-max · 16:9
prompt
Same room as in the reference image. Tight close-up of one window on the left wall. The window is tall, full-height from floor almost to ceiling, with a visible windowsill at waist height and a radiator below it. Dirty lace curtains hang from above. Strong backlight, bright overexposed light through the curtains. The same faded floral wallpaper visible on both sides of the window. Herringbone parquet floor at the bottom. Camera at eye level looking straight at the window. Empty room, no people. Same black and white cinematic style as reference.

Windowsill in place, radiator under it, wall visible, but backlight is weaker.

"How would you see the backlight? Flux did it logically — the window bright, everything else dark, exactly because the light blinds you. Light rays can't be visible head-on, they're seen from the side. I think we need to add legs to this gen somehow. We need a side angle of the boots, legs below the knee — a real detail."

Right physics: backlight blinds, no rays, Flux did it correctly.

Pose-ref for legs at 90°

body_ref is frontal, but the camera is now from the side — need a profile view of the legs. Seedream died on insufficient_quota (0.7 credits). Flux without death-words won't censor. body_ref as a ref for boot style.

body_ref_side_legs — one leg standing on the floor

body_ref_side_legs — one leg standing on the floor
params flux · flux.2-max · 16:9
prompt
Side profile view of the same man's lower legs and shoes as shown in the reference image, but viewed from the exact side (profile). Dark business suit trousers, same black leather dress shoes as in reference. The legs hang straight down vertically, motionless, toes pointing to the left. Only visible from mid-calf to shoes, nothing above. Plain neutral grey background. Black and white photograph, high contrast.

"One leg, standing on the floor. Need two legs in the air."

body_ref_side_legs_v2 — two legs, profile, in the air

body_ref_side_legs_v2 — two legs, profile, in the air
params flux · flux.2-max · 16:9
prompt
Side profile view of TWO legs in dark business suit trousers and the same black leather dress shoes as in the reference. Both legs hang vertically in mid-air, feet NOT touching any surface, clear empty space below the shoes. Legs viewed from the exact side in perfect profile, toes pointing to the left. Only visible from mid-calf to shoes. Plain neutral grey background. No shadow, no floor. Black and white photograph, high contrast.

"v2 is OK, but 1) the toes don't point cleanly sideways but slightly down (relaxed feet). 2) the light is sidelight, not backlight."

body_ref_side_legs_v3 — relaxed, backlit with rim light

body_ref_side_legs_v3 — relaxed, backlit with rim light
params flux · flux.2-max · 16:9
prompt
Side profile view of two legs in dark business suit trousers and the same black leather dress shoes as in the reference. Both legs hang vertically in mid-air, feet relaxed and drooping slightly downward (toes pointing down-left, not horizontal). Legs viewed from the exact side in profile. Strong backlight from directly behind the legs — bright rim light outlining the edges of the trousers and shoes, the front of the legs facing camera is dark. Only visible from mid-calf to shoes. Plain neutral dark background. No floor, no shadow. Black and white photograph, high contrast.

"Better, but the light needs to be totally backlit, this is sidelit-backlit. And the legs should partially overlap each other."

body_ref_side_legs_v4 — overlap, clean backlight

body_ref_side_legs_v4 — overlap, clean backlight
params flux · flux.2-max · 16:9
prompt
Side profile view of two legs in dark business suit trousers and black leather dress shoes matching the reference. Both legs hang vertically in mid-air, feet relaxed, toes drooping slightly down. Both legs are parallel and overlap each other in profile — the near leg partially covers the far leg, only one shoe toe visible in front. Light comes from DIRECTLY BEHIND the legs (pure backlight, no side component). The entire front surface of the legs is completely dark, only a thin bright rim of light traces the outer edge contour of the legs. Silhouette-like, very dark front. Mid-calf to shoes only. Dark background. Black and white, high contrast.

"Overlapping, but the feet are at 90°, the light isn't light, just a white outline."

body_ref_side_legs_v5 — soft light, overlap

body_ref_side_legs_v5 — soft light, overlap
params flux · flux.2-max · 16:9
prompt
Side profile view of two legs in dark business suit trousers and black leather dress shoes matching the reference. Both legs hang vertically in mid-air, parallel, overlapping in profile. Feet are completely relaxed and limp — toes drooping down at about 30 degrees below horizontal, like a dead weight. The front of the legs facing camera is dark. Subtle natural backlight creates a soft glow along the edges, NOT a sharp white outline. Mid-calf to shoes only. Dark background. Black and white photograph, high contrast, 35mm grain.

"Actually nah, good — if you can cut out the background well, they'll work for the window."

rembg → body_ref_side_legs_v5_cut.png. Pasted onto m2_window_tight_v2:

m2_legs_mockup — mockup with legs in front of the window (the user later remade the assembly in Photoshop)

m2_legs_mockup — mockup with legs in front of the window (the user later remade the assembly in Photoshop)

"I pasted the legs onto the window in Photoshop myself, same file."

"Seedream 4.5 written off"

Evolink still empty. Tried Flux edit on OR — ignored the mockup, drew a sitting man. Flux doesn't follow layouts the way Seedream does.

shot_legs_backlight_final — Flux, but drew a sitting man

shot_legs_backlight_final — Flux, but drew a sitting man
params flux · flux.2-flex · 16:9
prompt
Render this as one cinematic black and white photograph. The composition matches reference 2 — legs hanging in front of a bright window with backlight. The room style, wallpaper and atmosphere match reference 1. Blend the legs seamlessly into the room. Same 35mm film grain, high contrast, deep blacks.

The agent flipped the ref order — Flux took M1 as primary.

"Sorry, why the hell did you give Flux the wide room shot as the first picture?"

Reordered — mockup first, M1 second. Flux still put him on a stool:

shot_legs_backlight_v2 — refs reordered, but on a stool

shot_legs_backlight_v2 — refs reordered, but on a stool
params flux · flux.2-flex · 16:9
prompt
Render this as one cinematic black and white photograph. Keep the exact composition from reference 1 — hanging legs in front of a bright window. Blend the legs seamlessly into the room. The room wallpaper and style match reference 2. Same 35mm film grain, high contrast.

Seedream 4.5 on OpenRouter (bytedance-seed/seedream-4.5) — composition gorgeous, but bare legs and a different window:

shot_legs_backlight_sd45 — 4.5 OR, doesn't hold the refs

shot_legs_backlight_sd45 — 4.5 OR, doesn't hold the refs
params banana · bytedance-seed/seedream-4.5 · openrouter · 16:9
prompt
Render this as one cinematic black and white photograph. The exact composition from reference 1 — legs hanging in front of a bright window with backlight. Blend the legs seamlessly into the window scene. Same 35mm film grain, high contrast, deep blacks.

"Actually the stool variant is good, just need to remove the stool."

Attempt to remove the stool via Seedream 4.5 on OR — frontal legs, different room:

shot_legs_no_stool — stool removed but the scene was rebuilt

shot_legs_no_stool — stool removed but the scene was rebuilt
params banana · bytedance-seed/seedream-4.5 · openrouter · 16:9
prompt
Same image as reference 1 but the wooden stool is removed. The shoes and legs hang freely in mid-air with empty space and floor below them. Everything else stays exactly the same — same window, same curtains, same wallpaper, same lighting, same framing.

"Seedream said fuck it to the pose and to the room itself, you're clearly prompting it wrong."

Truth: 4.5 reinterprets refs instead of following the layout, this is a difference from 5.0-lite which on Evolink holds composition.

"Let's agree you don't use Seedream 4.5 at all, it's no good."

Written off. What's left:
- Seedream 5.0-lite via Evolink (holds layout).
- Flux 2 Max via OpenRouter (for location shots without violence).

"Will top up."

Day wrap-up

In five hours of work:
- shot1_video_door_kick.mp4 — first approved video with the bandit, the final 10-sec scene from session 1.
- The Seedance prompt form was formalized: Style:/Duration:/Camera: + @Image + timecodes + literal physics + negatives.
- Kling O3 tested: half the price and time of Seedance, but lower quality — for fast iterations or drafts, not finals.
- Discovered the MUAPI feature — @character:ID for persistent identity. Works only via MUAPI API directly, not piped through OR/Evolink.
- Found out that character sheets are for human verification, identity in video comes from character_id.
- Project moved into a separate git repo scripts/movie_experiment/ with the structure src/approved/archive/docs/.
- Gemini vision bbox joined the toolkit as an auto-cropper for character sheets (~1 cent per query).
- Prompt rule: literal physical descriptions, not metaphors and not states.
- Seedream 4.5 written off — doesn't hold refs, we use only 5.0-lite via Evolink.
- Prepared pose-ref body_ref_side_legs_v5 — profile hanging legs with relaxed feet. Waiting on Evolink top-up to assemble shot2_legs_backlight.