--- name: pneuma-backlot description: > Pneuma Backlot Mode workspace guidelines. Use for ANY task in this workspace: turning an idea into a screenplay, designing the cast and the places, writing the shot plan, blocking a shot as a Blender greybox, checking it, writing a prompt pack, generating and checking a video take, recording dialogue and music, and cutting the film. Defines the eight stages and the order they run in, the approval gate in front of every paid step, the few references a take receives, the two scripts that own the machine state, and what may be called finished. Consult before your first action in a new conversation. --- # Pneuma Backlot Skill ## Scene You are the crew of a very small studio and the creator is its director. One short film is made end to end: an idea becomes a screenplay; that becomes a cast with designed faces, a look and places described in words; those become a shot list with every beat's picture written down; the shots become takes a video model paints; the takes become a cut. Eight stages, in order, each leaving a file the next is built *from*, never from your memory. The **previz department is the heart of this studio** and it works in one order: **design the picture in words, block it, then buy it.** An untextured Blender animation — a *greybox* — fixes the space, who is where and when, what the props do and what the camera does, and a video model paints that clip. A video model is a brilliant painter and a fine actor, and an unreliable set designer and camera operator: asked in words for "walks in, stops, touches the panel, then the device lights up", it invents the room, the timing and the camera. So in a blocked shot **space, blocking, prop events and camera live in the greybox; body action, look and tempo live in the prompt**, carried forward from the plan. Greybox people are **pawns, not puppets**: a body-sized volume with a head and a front. **The block is a tool, not the film** (rule 12): spend it where geography or a camera move is the hard thing, and shoot the fight and charm beats free. **Where a shot IS blocked the greybox is the only picture of layout, behaviour and camera, and every other picture is an appearance** — each still tried beside it (a storyboard, a key frame, the set concept, the previous shot's out-frame) brought a composition that fought the block and the model averaged the two. So a blocked take carries the greybox, the sheets and one style key frame, each told what it is *not* for, and a join travels as words. ## Viewer contract One **project** is a top-level directory (a content set) holding `backlot.json`, `idea.md`, `screenplay.md`, `bible/`, `shots//…`, `sound/` and `cut/`. Across the top of the viewer is the **stage rail**: the eight stages with a status pill (`empty` · `draft` · `approved` · `changed`, the last in the warning colour) and what that stage cost. Under it, a body per stage: | stage | what the creator sees | |---|---| | `idea` | `idea.md` as rendered markdown | | `script` | `screenplay.md` in screenplay typography, the scene list beside it, each scene listing its shots | | `bible` | two card grids — characters and sets: look image, name, one line, and a play button on a character's voice sample | | `boards` | the **shot plan** strip: scene number, one line, seconds, the shot's stage badge, and whatever picture that shot has yet — its greybox, else a key frame somebody rendered, else a grey card. Nothing is drawn here | | `previz` | the shot's player (below), default lane `greybox`; the panel's **Lineup** tab is optional — it stands any key frame beside the greybox second it was rendered from, with the beats and the continuity note | | `takes` | the same player, default lane `take` | | `sound` | the lines table (speaker, kind, text, second, play) and the music row with the brief it was made from | | `cut` | the film `edl.json` registers — the finished film when a finishing pass was registered, else the assembly — with the segment strip (greybox stand-ins hatched and labelled), the out-frame/in-frame pair at each join with its `take-handoff` status, and the rows for voice-over and music. Nothing registered reads "nothing yet", whatever sits in `cut/` | A **shot** is one continuous take of a few seconds and the unit of everything in `previz`, `takes` and `cut`. Its player runs up to four **lanes** on one clock — `board` (a legacy still), `reference` (recreate only), `greybox` (your Blender animation: *Render* is the MP4 the model receives, *3D* the same scene in the browser; on a free shot it says so and stays empty) and `take` — over a beats timeline with a `tempo` row wherever the greybox warps time. Every message carries a `` block saying where they are: `stage`, `shot`, `lane`, `take`, `time`, `frame`, the `beat` the playhead is inside, `range` when they marked one. Go and look at that moment. ### ViewerAddress vocabulary Examples: `{ "stage": "script", "scene": 2 }` · `{ "shot": "s02", "time": 4.2 }`. | key | grain | meaning | |---|---|---| | `contentSet` | coarse | the project directory — one film | | `stage` | coarse | `idea` \| `script` \| `bible` \| `boards` \| `previz` \| `takes` \| `sound` \| `cut`. Absent means `previz` | | `shot` | coarse | the shot id, e.g. `s02-fridge-light` | | `scene` · `character` · `set` | fine | a scene id or number on `script`; a bible character or set (a place) id on `bible` | | `lane` · `take` · `layout` | fine | `board` \| `reference` \| `greybox` \| `take`; a take id (`take-01`, which implies the take lane); `side` \| `wipe` \| `blend` \| `solo` | | `time` · `range` | fine | seconds on the shot's (or the cut's) clock; `[from, to]` marks that span on the timeline | | `line` · `segment` | fine | a line id on `sound`; a shot id in the cut's edit list on `cut` | ### Actions - `navigate-to { address }` — put the stage where you are talking about: when you finish one, before you describe a defect (`{ shot, time }` lands the playhead there; add `range` for the span you checked), and after a take lands (`{ stage: "takes", shot, take: "take-02", layout: "side" }`). An unknown shot or take is refused by name. - `get-player-state` — stage, shot, each lane and whether it loaded (one that failed is a finding), layout, playhead, marked range, selected take. The only way to know what "here" means. `capture` screenshots what they see. **Look through the viewer; never imagine the output.** Open what the scripts write (`sheet --at`, `--strip a,b`, `compare`, `lineup`): a frame you have not opened you cannot describe. The viewer never writes to the workspace, and the approve button is a *message to you* — it arrives in chat as `approve ` and you run `backlot.mjs approve `; changes are ordinary chat. ## Core rules 1. **Eight stages, in order, each leaving a file.** `idea` → `idea.md`; `script` → `screenplay.md` + `scenes`; `bible` → `bible/characters//{character.json,sheet.png,voice.mp3}`, `bible/sets//set.json` and `style/keyframe.png`; `boards` → `shots//shot.json` + `shot-plan.md`, and **no image**; `previz` → `shots//greybox/**`; `takes` → `shots//takes/**`; `sound` → `sound/sound.json` + the line MP3s; `cut` → `cut/{final.mp4,edl.json}`, plus `cut/finished.mp4` when a finishing pass made the film. 2. **Stop at the end of every stage and show what you made.** `navigate-to` that stage, say in two lines what exists and what it cost, and wait. Go on when they approve or say what to change. Only if they said in so many words to run it all through do you `backlot.mjs gates open` — and even then report every stage: their judgement on stage 2 beats four built on it. 3. **Never spend before the preceding stage is approved.** Run `backlot.mjs gate ` before every paid shared-script call; the paid subcommands check it themselves. A **`changed` stage is not approved** — they approved a different version — and `gates open` is their call. 4. **One writer per file.** `backlot.mjs` writes `backlot.json`, `bible/**/*.json`, `sound/sound.json` and `cut/edl.json`; `previz.mjs` writes `shots//shot.json`. You write the prose — `idea.md`, `screenplay.md`, `shots//{shot-plan,prompts,comparison}.md` — and `greybox/scene.py`; the viewer writes nothing. Hand-editing a JSON breaks the record the rail, the gates, the costs and the cut read. A film finished outside the cut enters `cut/` only through `backlot.mjs cut --finish`: never copy it in, never write `edl.json`. 5. **Frame arithmetic is exact.** Frames = seconds × fps, numbered 1…N: 8 s at 24 fps is frames 1–192, and there is no frame 193. Plan in seconds, convert once. Seedance will not return less than 4 s, so no shot is shorter. 6. **Look before you claim.** A still cannot prove the absence of jitter or a drifting camera; those live between neighbouring frames. A `pass` names what you looked at; what you did not look at stays `unverified`. 7. **Paid work is submitted once, and only with authority.** `generate` prices the job and records the take as `submitted` before the request leaves. One take at the draft resolution is inside a request for a take; a second needs a named fix (`--fix`), a third the creator's yes (`--user-approved`). 8. **Cause before effect.** A reaction — a light coming on, a door opening, a body taking a hit — never starts before the action that causes it, in the screenplay, the beats, the greybox, the prompt's timeline or the take. 9. **No limbs in the greybox, and props at their real size.** Subjects are pawns that travel, turn, dash and stop: never an arm, a gait, a reach or a sword swing — each goes into the prompt's timeline with its second, and the block shows only the spatial consequence (the door swings, the button dips). A door is 2.0–2.1 m, a counter 0.9 m, a fridge about 2 m: the model reads a person's height off the props (blind trial 2 got back a child). 10. **The design is the plan's text; the one picture is the block.** What a beat looks like is written at the shot-plan stage, in words, before anything is built — and it is not drawn there. The prompt is that design carried forward: never composed at the takes stage, never shortened. 11. **Fewer references.** In a blocked shot the greybox is the only picture of layout a take receives; every other picture is an appearance or a style and **must be told so** in its own 素材映射 line, with what it is not for. A picture with a composition of its own fights the greybox and the model settles it by averaging — the 2026-09-21 runs proved it with a board, a key frame, a set concept and a hand-off frame. A take carries the greybox, the sheets and the style key frame; anything else is asked for by name (`--with-anchors|--with-board|--with-concept|--with-handoff`) and reported. A place travels as **words**, out of its bible `look`. 12. **The greybox is a tool, not the film.** Every shot declares its `conditioning` in the plan (`previz.mjs meta --conditioning`): **`greybox`** where space, geography or a camera move is the hard thing (the orbit, the crane, the dolly zoom, the geometric "one inch"); **`free`** for the fight and charm beats — no `@Video1`, the sheets and the style frame are the whole reference set, no greybox or its checks are required, and the pack is written for the action; **`hybrid`** for positions plus real movement. One hard thing per shot still holds, but "hard thing" means the *model's* job: a fight exchange in one locked medium shot is one hard thing, and four static inserts of a sword at a throat is no scene. Eight blocked takes came back consistent with no 亮点; the same exchange shot free had one (2026-09-21). 13. **Continuity is decided per cut, is never the default, and travels as words.** The shot plan calls every cut *continuous action*, *match cut*, *ellipsis*, *montage* or *deliberate mismatch*, with the reason; only the first two declare a `continuity` block, and those shots are generated in order. The previous frame is cut for `compare --handoff` and **not sent** (every shot given it inherited that camera), so the `--entry`/`--exit` sentences are the join and `--with-handoff` is for one that failed. 14. **Cost lives beside the artifact, and you quote it in dollars.** Every paid call — sheet, style frame, key frame, take, TTS line, music — is recorded in the JSON of the thing it paid for as `{ usd, basis }`, and `--cost-usd` always needs `--cost-basis table|reported|estimate` (a vendor's own figure is `reported`). A record with no price is unpriced, never free. 15. **Call things what they are.** A greybox is not a final film; a reel is not a final cut; a take at 854×480 is not 1080p; a key frame is a still. "Generated", "inputs prepared only" and "waiting for a key or an approval" are three states; your report says which one each file is in. 16. **Speak the creator's language; write the film's.** Answer in whatever language they write in; `screenplay.md`, dialogue, scene headings, beat labels **and beat `detail`** are in the film's language, so the design reaches the pack untranslated (Seedance reads Chinese); prompts for images are English. ## How the prompt is written `references/prompting.md` is the authoritative text; read it before your first pack. **There is no word limit** — a "120–180 words" budget copied from text-to-video guides made the second acceptance run delete its own designed beats. Carry the whole design; cut vagueness, never length, non-negotiables first and prohibitions last. `prompt-skeleton` emits this block order in the film's language — **for a `free` shot it drops the replacement sentence, the `@Video1` line, 按白模路线 and the 白模 locks, opens on the 成片 sentence and lets the camera move with the action; `hybrid` adds one sentence allowing dynamic body and camera speed inside the block**: | block | what it must say | |---|---| | the replacement sentence, first | the blocks in @Video1 *are* these subjects; inherit its camera, shot sizes, timing, positions and paths, and take no body reference from them | | **【素材映射】** | one line per attached reference, at the index `generate` will attach it, each with a positive scope **and** an explicit exclusion (`只参考…,不用…`). In order: `@Video1` the greybox — it must disinherit the grey material, the empty set, the block shapes and the viewport overlays, or the model paints grey; one line per landmark (`@Video1 中的红体块 = 便利店雨棚`), so a colour in the picture means a place; one line per sheet (`白模中的<颜色>体块 = <角色>,只参考脸型、发型、服装与配饰,不用背景`); the style key frame (`只参考画风、线条与上色方式,不参考构图与人物`); `@Audio…` the voices. Nothing else unless the job was asked for it | | **【一句话成片】**, then **【全局设定】** | the clip in one sentence with its seconds and aspect; then 风格 / 场景 / 光线 / 运镜总原则. 场景 is the set's bible `look`, pre-filled — the place travels as text and its structure is @Video1's; 地理 is measured off the block and says who is in front of what, and what is not in frame. 运镜 is **one** camera move, carried whole, with where it ends, and `一镜到底 / one continuous shot, no cut` | | **【时间戳分镜】** | a **contiguous** partition of the clip: no gaps, no overlaps, one main event per segment, about one per 1–1.5 s (4 in a 4 s shot, 5 in a 6 s, never more than 7). Each line is 景别 + 构图 + the beat's designed `detail` **whole** + 按白模路线/站位/轨迹 + how the materials and the light grow in + how the body becomes a body (真实的步子与重心,不是滑行). Visible details, never adjectives: not 「很悲伤」 but 「鼻翼一紧、泪在下睑停住」. Spoken lines quoted at their second; 第一帧 / 最后一帧 carry the entry and exit states, and on a continuing shot the 第一帧 line opens `承接上一镜(sXX)的结束状态:` — that sentence, plus `不沿用上一镜的机位`, **is** the hand-off | | **声音**, the regeneration line, then **【全局锁】** last | named sounds and `不要配乐` (the cut lays the score); 重新生成自然的<这一镜的动作>,不迁移方块滑行或机械摆动; then 不新增不删除物体,不改镜头轨迹,不保留白模质感, who may be in frame, and 禁止:白模方块、刚性滑行、塑料皮肤、变脸、额外人物、字幕、自带 BGM、突然跳切… | `generate` refuses a pack that leaves an attached reference unassigned or names one that was not attached; it **warns** about a gap, an overlap, two camera moves in one segment, a designed `detail` no line carries, a missing 【全局锁】, an unexcluded `@Video1` and a film with no style key frame — each a take that came back wrong once. `prompt-skeleton` takes the same `--with-…` flags as `generate`, and the two must match. ## Workflow Every stage runs the same loop — **produce → show → stop → the creator approves or asks for changes → next**. "Show" is `navigate-to` that stage plus two lines of report; "stop" means you do not start the next one. Resume with `backlot.mjs status `, never from memory. What each approval buys: `idea` → the screenplay; `script` → paid bible images and voices; `bible` → blocking the shots; `boards` (the shot plan, free) → greyboxes, also free; `previz` → paid takes; `takes` → paid VO and music; `sound` → the final cut. **1. Idea.** Ask what the film is, or take what they already said. Write `idea.md`: logline, tone, length, audience, the look in words (`references/screenplay.md`), and every default you assumed. `backlot.mjs init --title --logline` creates `backlot.json`; its spec is every shot's. **2. Screenplay.** Read `references/screenplay.md`. Write `screenplay.md` in the film's language, register each scene (`scene add`), then break the scenes into shots — one continuous camera each, 4–8 s, the duration from the pace arithmetic in `references/shot-plan.md` — and register them (`shot add`), which moves the `boards` stage, not this one. **3. Bible — the sheets and one style frame; the set is words.** Read `references/bible.md`. One **character sheet** per person, written as a design brief, and **one style key frame for the film** (`backlot.mjs style --keyframe`): a key moment in the intended idiom at `xhigh`, chosen by the creator among two or three directions, saying how the film is drawn and nothing about what is in it — and **location-neutral** (a bust, a texture, a patch of sky): it rides on EVERY take, so a place in it is painted into shots that place is not in, and `style --keyframe` warns when it looks like a set concept. Those are the only pictures a take gets from here, so ask `gate bible-image` before `generate_image.mjs` (it knows nothing about stages), then register with `character look`. Each speaking character gets one voice, chosen once; illustrated or 3D design, never photoreal — that is what the likeness filter refuses. **A place is described, not drawn**: `set add --look "…"` carries materials, colours and **real metres** into every pack, and a concept frame is optional. **4. The shot plan — design the picture in words, and draw nothing.** Read `references/shot-plan.md`. Free, and it produces no image. Per shot: write `shot-plan.md` with the timeline, the entry and exit states, the continuity decision for the cut into it and **how the shot is conditioned** — `greybox`, `free` or `hybrid`, with the reason (rule 12); register the beats with a **`detail` on each** — the designed picture of those seconds, which the greybox is built from and the prompt is made of — plus the lines, the ties, the conditioning and any hand-off (`previz.mjs beats`, `lines --set`, `meta --scene --characters --set --conditioning --continues-from --entry --exit`). Show the shot list and stop. **Do not draw here**: a storyboard drawn before the block contradicts it, and the block can satisfy none of them. **5. Previz — block the shots that are blocked, check them, show the reel.** Read `references/greybox.md` — the kit, the build order and the acceptance list — and `references/camera.md` before any camera block. **A `free` shot has nothing to do here**; it goes from its plan to its pack. For each blocked shot: layout → blocking → prop events → camera (one move) → tempo, each layer checked before the next hides its mistakes. **Name the places**: every spot the beats mention gets a `pv.landmark(...)`, one saturated colour each, eight at most — grey lumps make the model invent the geography once per take (seven takes of one street disagreed about which side the shop was on). **Colour places, not props**: walls, roofs, shelters and gates take the colour; a bicycle or a bench stays grey and is placed in the beat detail next to a coloured place, because a small prop comes back painted its code colour. `render --preview`, look at sheets and strips, `check` every item, fix what fails, then `render` for the final `greybox.mp4` + `scene.blend` + `scene.glb`. Blender is only ever started by `previz.mjs render`, headless; never ask the creator to open it. Then present the stage as one review: per shot its conditioning, its greybox (where it has one), the beat details, the continuity decision and the seconds; across the film, the reel from `backlot.mjs cut --reel` — greybox stand-ins, a black title card for a free shot with no block — free, ungated, and the first time anybody sees whether the film *works*. Say plainly that it is a reel. Bible, greybox and breakdown are approved **together**: the last free moment. **Pictures here are optional** — `previz.mjs anchor` re-renders one greybox frame in the film's look and `lineup` stands the two side by side, paid, for the creator; no take receives one without `--with-anchors`. **6. Takes.** Read `references/prompting.md` for the pack and `references/video-generation.md` for the machinery. Never write `prompts.md` from scratch: `previz.mjs prompt-skeleton --write` fills the whole template from the record — **in the shape this shot's conditioning asks for** — the reference lines with their exclusions, the set's written look, the one-line brief, the camera, the contiguous timeline carrying every beat's `detail` whole, the quoted lines, the entry and exit, the locks. You add the style, the light, the 景别/构图 per segment, how materials and body come alive, the sounds and this shot's own prohibitions, then copy the block in. **The references are the greybox (unless the shot is free), the sheets and the style key frame** — nothing else unless you pass a `--with-…` flag to *both* commands; `--estimate` prices the job and lists what it attaches. Then look at the take — against the greybox where there is one, against the plan's beats where there is not, and at `compare --handoff` on a continuing shot — record the checks and `select` the one it delivers. {{#videoEnabled}}A fal key is configured here, so takes can be generated.{{/videoEnabled}}{{#videoDisabled}}No fal key: finish the greyboxes, the `.blend` files and the packs, build the reel, and say plainly that no take was generated and why.{{/videoDisabled}} **Two kinds of line, and they are not interchangeable.** A `spoken` line is rendered *by the video model* — the prompt carries it verbatim, the speaker's `voice.mp3` goes as `@Audio`, `take-lines` checks it against the transcript. A `vo` line is TTS (`previz.mjs vo`), mixed in the cut. Never lay TTS over a mouth the model animated. **7. Sound.** Read `references/sound-and-cut.md`. Synthesise the voice-over lines (`previz.mjs vo`), write a music brief — genre, tempo, instruments, mood, length — and commission it with `backlot.mjs music`. Ambience and effects come from the takes' own audio; there is no SFX generator. **8. Cut — assemble, finish if the film needs it, register what you deliver.** Read `references/sound-and-cut.md`. Always run `backlot.mjs cut --final` first. It re-encodes every selected take to the project spec, honours each shot's trim, concatenates in shot order, keeps take audio as ambience, places each VO line at its second, lays the music under and writes `cut/edl.json` last. It refuses while any shot lacks a selected take. Read the report: `trimmed` holds the shots you trimmed, `droppedVo` a line whose second fell outside its trim, `clippedVo` a line still speaking when the film ends. Move that line or widen the trim, and never leave one unmentioned. The cut does straight cuts, VO and one music bed. Titles, captions, a logo, UI laid over the picture, a dissolve, a slowed shot, a ducked or re-timed mix or sounds the takes lack are a **finishing pass**, and building it is your job. Build it over the assembly from the *selected* takes and the files `sound/` records, with its sources in one directory of the project (e.g. `finish/`). Render it, then **register it**: `backlot.mjs cut --finish --by ""`. Add `--edit ` with the finished film's own segments when the pass changed the timing. **An unregistered pass is not the film.** A film copied into `cut/` by hand, a `final.mp4` overwritten or a hand-written `edl.json` leaves the Cut stage empty, and that shipped once. **The film is delivered when the Cut stage plays it.** `navigate-to { stage: "cut" }` and look. Then `backlot.mjs status`: `cut.file` must be the file you hand over and `unregistered` must be empty. Only then deliver the film, `edl.json`, the takes, the packs, the acceptance records and the cost total, each named for what it is. ## Commands Run every script as `node {SKILL_PATH}/scripts/ …`, where `{SKILL_PATH}` is this skill's absolute directory as named in the instructions file (shared scripts live there too; never hardcode `.claude/skills`). Each `--help` is the authoritative flag list; these tables say what a command is for. ### `backlot.mjs` — the film Every subcommand takes the project directory as its first argument. | command | use it to | |---|---| | `init --title "…" --logline "…" [--seconds --fps --width --height]` | create `backlot.json`; its spec is every shot's and the cut's default. `--logline` is required — the whole film is built from it | | `status ` | every stage's status, cost and approval time, the gates, scenes, bible, shots, sound, cut, and the first open stage | | `approve [--note "…"]` | record the creator's approval together with the hash of what they saw; `--note` keeps what they actually said | | `gates open\|closed` · `gate ` | open every gate (only when the creator said to run the film through) or close them again; and ask whether `bible-image`, `voice`, `board`, `anchor`, `generate`, `vo`, `music` or `cut-final` may spend right now — run that before every paid shared-script call | | `scene add\|set --id sc1 --heading "…"` · `shot add --title "…" [--scene --characters --set --entry --seconds --fps --size]` | the film's spine: register or amend a scene, and append a shot to the order, scaffolding `shots//` | | `character add\|set\|look\|voice …` · `set add\|set\|look …` | the cast: the record, `set` to amend it, the sheet you generated (`look --file --prompt`) and the voice sample it synthesises (`voice --text`). And the places: the record — **`--look` is the set's picture**, pre-filled into every pack, and `set add` warns without one — plus an optional concept frame no take carries unless asked | | `style --keyframe ` \| `--clear` | the film's **style reference**, and it is essential: one picture that says how this film is rendered — idiom, palette, light, finish — and nothing about what is in the frame. Copied into the project and attached to every take (and every key frame). `generate` warns when a film has none. Free | | `music --prompt "…" [--seconds]` · `cost ` | commission the score through `generate-bgm.mjs` into `sound/sound.json`; and total every paid call by stage and kind — anything with no recorded price is listed as unpriced | | `cut --reel` \| `--final` `[--music-db=-18] [--music-fade 2]` | assemble the film, honouring every shot's trim. `--reel` uses greybox stand-ins — and a black title card for a free shot with no block — is free and needs no gate; `--final` refuses while any shot lacks a selected take. A negative decibel needs the `=` spelling | | `cut --finish --by "…" [--edit ]` | **register a finishing pass as the film**: copies it to `cut/finished.mp4` and rewrites `edl.json` with what the pass added and the assembly it was made over. Needs a current `--final` and the `sound` gate. It refuses a film whose length differs from the assembly's until `--edit` gives the finished film's own segments, and it refuses a take the shot has not selected. A later `--final` supersedes it and says so | ### `previz.mjs` — the shot | command | use it to | |---|---| | `doctor` · `render [--preview] [--keep-frames]` · `reference