← Plugin catalog
Creativity

Picsart

Picsart v2.1.0

Follow this plugin to keep it in your list and review observed changes. Free account · Up to 10 plugins · No card required.

What changed

Observed changes, with before-and-after evidence
Picsart

Oct 9, 2026 · 3 saved observations

Metadata · Oct 9, 2026 · 18:02 UTC · Compared with Sep 30, 2026 · 22:45 UTC

WHAT CHANGED · RULE-BASED ANALYSIS

Declared capabilities changed

Declared skills changed from “[]” to “[{"description":"Make a short narrative AI film — multiple scenes, recurring characters, a locked visual style — through a guided 11-stage production pipeline with gates. Use when the user wants to make a film, short film, movie, episode...”.

Observed in instructions or declared skills. Runtime behavior has not been tested.

Short excerpts from up to four changed fields. Open the full comparison for all changes.

Declared skills

Before

Sep 30, 2026 · 22:45 UTC

[]

After

Oct 9, 2026 · 18:02 UTC

[{"description":"Make a short narrative AI film — multiple scenes, recurring characters, a locked visual style — through a guided 11-stage production pipeline with gates. Use when the user wants to make a film, short film, movie, episode...

App-6a18d4c190a0819186e6b129a09e931e

Before

Sep 30, 2026 · 22:45 UTC

{"id":"asdk_app_6a18d4c190a0819186e6b129a09e931e","required":true}

After

Oct 9, 2026 · 18:02 UTC

Not present

Picsart-gen-ai

Before

Sep 30, 2026 · 22:45 UTC

Not present

After

Oct 9, 2026 · 18:02 UTC

{"id":"asdk_app_6a18d4c190a0819186e6b129a09e931e","required":true}

Id

Before

Sep 30, 2026 · 22:45 UTC

pluginrel_634a2f9782348191a996949ba2dfb361

After

Oct 9, 2026 · 18:02 UTC

pluginrel_f5512779980c8191bc3fb60b808fcb7b

Metadata: full comparison →Listing: full comparison →

Files · Oct 9, 2026 · 18:02 UTC · Compared with Sep 30, 2026 · 23:19 UTC

WHAT CHANGED · RULE-BASED ANALYSIS

Package or technical metadata updated

Package contents changed in 64 files: .app.json, .codex-plugin/plugin.json, .mcp.json, …. Open the file diff to inspect the edits.

Observed in package metadata. These changes alone do not establish a new customer-facing feature.

Short excerpts from up to four changed fields. Open the full comparison for all changes.

File entries below show saved metadata, such as size and fingerprint. Open a changed file in the full comparison to read its text edits.

Package file

Before

Sep 30, 2026 · 23:19 UTC

{"sha256":"9299c61aac9f097c99a6cfba84353b7c630386a58e8ddf5f4b884467be68fc56","size":127}

After

Oct 9, 2026 · 18:02 UTC

Not present

Package file

Before

Sep 30, 2026 · 23:19 UTC

4f4a7f8a6624b4ce02fd10592c3ec97ccebb2612951cc8371776a8c8c04eb013

After

Oct 9, 2026 · 18:02 UTC

b14504444f96665ad2ba260272d569b667cdf4aa2a2805bf551ccbd578d834f3

Package file

Before

Sep 30, 2026 · 23:19 UTC

1351

After

Oct 9, 2026 · 18:02 UTC

9368

Package file

Before

Sep 30, 2026 · 23:19 UTC

Not present

After

Oct 9, 2026 · 18:02 UTC

{"sha256":"4b26137c7a95699fd11a04b3b804e5e705a039f77420344a3cc68236ee7f0f64","size":106}

Compare changed files →

Publisher description

From the marketplace listing

Create and edit images, videos, audio, and text with Picsart AI models. Compose and render media scenes, inspect model parameters and credit estimates, track generation jobs, and browse, save, and organize files in Picsart Drive.

Language: English · Automatically detected from descriptions.

Matches for “SC”

Exact text from the indicated source. A mention alone does not establish support for your task.

Publisher description

Create and edit images, videos, audio, and text with Picsart AI models. Compose and render media scenes, inspect model parameters and credit estimates, track generation jobs, and browse, save, and organize files in Picsart Drive.

Files & skills

File archives

Plugin package63 files · 412 KBBrowse files →
Skill instructions
film60.5 KB

View saved version →

---
name: film
description: "Make a short narrative AI film — multiple scenes, recurring characters, a locked visual style — through a guided 11-stage production pipeline with gates. Use when the user wants to make a film, short film, movie, episode, or multi-scene story (\"make a short film about…\", \"I have a script\", \"a movie with two characters\"). Not for a single clip from a description, and not for stitching footage the user already has — each of those is a direct tool call, not an eleven-stage pipeline."
---

# ChatGPT and Codex integration

Apply these host conventions throughout this skill and its references.

- Discover the connected Picsart tools and use their actual names and schemas.
  The `picsart_*` names in the workflow identify tools, not a fixed host namespace.
  If the connection is missing, explain that Picsart must be connected; continue
  script and shot planning when possible. Never invent tool results or renders.
- Read board decisions from the current user message and attached widget context.
  Use `read_widget_context` only if the host actually exposes that tool. Do not
  assume ChatGPT has Claude's context reader. Treat a board's initial tool result
  and unsent widget state as previews, never as user approval. If a submitted
  decision has no accessible payload, explain briefly and use the text fallback
  to collect the missing decision; do not guess or repeat charged operations.
- Use the host's available question interface or one concise conversational
  question for preset choices.
- UI rendering and delivery of feedback are the remote MCP server's responsibility.
  If a board is unavailable, present its relevant options in text and record the
  user's selection using the same documented feedback fields. Do not claim that
  a plugin manifest makes Claude-specific widgets compatible with ChatGPT.
- Save `film.json` and artifacts in the user's writable project workspace, not
  the installed plugin directory. If persistent file tools are unavailable,
  keep a structured project checkpoint in the conversation and provide it for
  download when the host supports files. Never claim a file was saved without
  confirmation from a file tool.
- Honor the user's current instructions and already authorized scope. Preserve
  quoted spend limits and production locks; do not infer authorization from a
  preview, a timeout, or a connection being installed. A timed-out charged call
  needs a status check before considering a retry.
- This package holds six film skills (`film`, `film-development`, `film-assets`, `film-scenes`, `film-edit`, `film-finishing`) and three motion skills (`motion`, `motion-import`, `motion-design`). Route between them directly.
- `motion-import` reads a design's layers through the Figma app's tools. If Figma is not connected in this host, use its flat-frame fallback and say so once.


# Film — the production pipeline, orchestrated

A film is not a long video. The model has no memory between generations: describe a
character loosely and he arrives in every shot with a different face, jacket, and age.
The whole pipeline exists to beat that one problem — **consistency** — and it runs on
one economic law: **stills are cheap, video is expensive.** Every decision that can be
made on text or images is made there, before a single charged video call.

Plan free → one approved sheet per asset → native draft when supported, otherwise a price-saving resolution draft (see `../film-scenes/SKILL.md`, *Native draft to final*) →
pay full price exactly once, at the final pass. The same law,
stretched across a production.

## 0. Triage — route before anything else

| User brings | Route |
|---|---|
| One clip's worth of idea ("a video of a boot on scree") | **not this pipeline** — one `picsart_generate` video call, quoted and approved first; the eleven stages exist for recurring identity, and a single clip needs none of them |
| Footage to stitch | **not this pipeline** — the scene tools direct: probe → author → validate → `picsart_scene_editor` on the built scene → `picsart_media_export`, written out in `../film-edit/references/ASSEMBLY.md` |
| A story: scenes, characters that recur, dialogue | **this skill** |
| An existing film project (a `film.json` or a workspace dir) | resume — read it, say where the film stands, continue at `phase` |

A 30-second ad with one recurring product is a single generate call with a
reference image. A 90-second story with two brothers who must look like themselves
in every scene is a film. The dividing line is *recurring identity across scenes*.

*(Until 2026-09-10 the first two rows routed to a `picsart-video` plugin —
`video-from-idea` and `video-from-clips`. It was retired as obsolete; both jobs are
a direct tool call, and the scene editor and the compositor are the same ones this
pipeline uses.)*

## Pace — director by default

Default to **director pace**: keep the user involved at each stage gate, show
the key craft decisions before progressing, and preserve the full production
ceremony. Mention once at project start that **express pace** is available if
they want you to make the routine craft decisions and bring them in only for
taste verdicts on boards, the three locks that shape everything (the story
and the video plan, the look, the assets), every charged video dispatch, and any spend beyond
the quoted plan. Switch to express only when the user asks.

Pace changes approval cadence, never safeguards: express pace still uses every
required board, respects all three locks, and asks before any
spend that requires confirmation — including **every charged video dispatch,
quoted with `picsart_preflight` and dispatched only on the user's yes
(2026-09-07)**, whatever the pace.

## How you talk to the user

Conversation rules:

- **Read submitted widget feedback before acting.** Follow the host conventions above and `references/WIDGET-FEEDBACK.md`; never treat the board's initial result as user approval.

- **Say only what the user needs in order to decide.** The pipeline is your
  bookkeeping, not theirs. It lives in `film.json`; a chat turn is for the
  decision in front of them. Four things that keep leaking into prose and should
  not:
  - **retrospective accounting** — "Stage 2 spent nothing, which is the
    short-film default". They did not ask what the last stage cost. Quote what
    the *next* thing costs, when it costs something.
  - **how the machine works** — "the tone moodboard isn't a separate purchase,
    it's the booth's first plate at Stage 4 doing two jobs". True, internal,
    and nothing they can act on.
  - **editorial subtitles on a stage** — "the last free stage before anything
    renders". A stage name is a locator, not a headline needing a subhead.
  - **standing offers nobody asked for** — "if you have reference images, drop
    them in at any point". Say it when it is relevant to the choice being made,
    once, not as a footer.

  A turn that is two sentences and one question is doing its job. If a paragraph
  is explaining the pipeline to justify what you are about to do, delete the
  paragraph and do it.

- **One decision per turn.** End a turn with at most one question the user must
  answer *now*; park everything non-blocking in `film.json`'s
  `openDecisions` and raise each only when it actually blocks. "Three things I
  need from you" is how users get stuck — they answer one, and the rest dangle
  into re-asks.
- **One confirmation covers a set.** Things presented together get one
  "anything to change?" — never per-item sign-offs (five scene shotlists is one
  approval, not five). Boards exist for targeted edits, not sequential
  ceremony.
- **Free work never asks permission.** Opening a board, drafting descriptors,
  drafting the run's prompt, updating `film.json` — do it; never end a turn
  with "want me to open them as review boards?". Permission questions are for
  money: every charged video dispatch — its own preflight number in front of
  the user, then their yes (`film-scenes`) — and any spend beyond the quoted
  plan. The optional next-video recap below is a navigation choice, not a
  permission request or another approval gate.
- **Offer a memory refresh after a long video iteration.** When moving to the
  next video after substantial time spent generating, reviewing or editing the
  previous one, or resuming that handoff in a later session, briefly offer once:
  "I can open the shotlist to refresh what comes next and check how it connects
  to the approved edit, or we can go straight to shooting." Use the actual video
  numbers when known. Do not automatically open the board or turn this into a
  mandatory sign-off. If the user already chose straight to shooting, honor it
  without repeating the offer; a routine uninterrupted handoff needs no recap.
  If they choose the recap, reopen the existing shotlist board with the next
  video's shots and explain the join from the **latest approved edited ending**
  of the previous video, not an obsolete script endpoint or untrimmed source.
  Preserve approved dialogue and shot decisions unless the user requests a
  change. If they skip the recap, continue preparation without reopening it;
  continuity checks, reference checks and charged-dispatch approval still apply.
  Record the offer and their choice in the project checkpoint so a new session
  does not ask again for the same handoff.
- **Progress is never damage — and say it without the shop talk.** Mid-stage
  state is "not yet", never failure language: an empty coverage table during
  Stage 4 is normal, not an alarm. But reassurance in jargon reassures nobody.
  *"Scene × assets — every cell is a hole right now, and that's Tuesday
  afternoon, not an alarm. Holes only bite at generation time"* is four internal
  metaphors in two sentences, and a user who cannot parse it cannot be calmed by
  it. Say the plain version: **"nothing here is ready yet — that's expected at
  this stage, and it only matters once we start shooting."** Call the table what
  it shows — which scenes have everything they need — not "the matrix", and an
  empty cell "not ready yet", not "a hole". Alarm words are reserved for a real
  gate violation (charged video attempted through a missing asset). A user told
  "zero locked assets" in a grave voice hears "you wasted an hour", and nothing
  was wasted.

## The pipeline — 11 stages, 5 working phases, gates between them

Stage numbers are the shared vocabulary; use them even though skills group them.

| Phase | Stages | Skill | Gate to pass |
|---|---|---|---|
| A · Development | 1 Breakdown · 2 References · 3 Bible | `film-development` | descriptors drafted, style prefix compiled (the lock itself is the sheet approval in B) |
| B · Assets | 4 Asset sheets · 5 Library | `film-assets` | every sheet approved on one board = `locked`; every scene's tags covered |
| C · Scenes | 6 Generation | `film-scenes` | all shots `final`, in selects |
| D · Edit | 7 Edit (8 Cleanup folded in) | `film-edit` | the cut the user called final |
| E · Finishing | 9 Color · 10 Sound · 11 Master | `film-finishing` | masters exported, archive saved |

**Stage 1 runs in a fixed order:** idea → look console with frame and
runtime pre-set, locked → story and video-model choice in one response when
the host supports it → one shot list board carrying the video plan's run
assignments and joins. The submitted board approves both together.
`picsart_film_setup` comes before a word of story because genre
decides how the story is told, and because every shot inherits the locked
style prefix, lens family and colour scheme. The model board comes after the story and before
the video plan because the plan is written against the model's ceiling and
reference budget. A note about what happens, at any stage, is answered with a
rewritten story paragraph, never a board (`film-development`). Store the returned
`duration` seconds in `film.json.target.duration`. When the user has said
anything about format — vertical, TikTok, square, scope — pass it as
`suggested.selections.ratio`, in whatever words they used (`vertical`, `9:16`
and `vert` all resolve); the console cannot infer it and opens on 16:9. If the
genre changes at any point, the story is rewritten first
(`film-development`).

**Gates are the product.** The most expensive mistake in AI film production is
generating video for a scene whose assets are not locked — half the material gets
redone. A gate is passed only when its checklist (in `references/PIPELINE.md`) is
met, or when the user consciously overrides it — record the override in `film.json`
with a reason. Never override silently on their behalf.

**A stage is never finished by assertion.** Before you say a stage is done, or
name the next one, print its gate checklist from `references/PIPELINE.md` with a
✓ or ✗ per line and the evidence beside each ✓ — a status a tool returned, a
recorded waiver, a user's yes. If any line is ✗ the stage is not done, and the
sentence to write is which line and what closes it, not the next stage's name.
Skipping is allowed when the user says so; skipping *silently* is not, and
neither is a stage nobody mentioned: if the path from here to there passes a
stage, name it, even to say it is being skipped and why.

This is a reported failure, not a hypothetical. Five assets were saved at
`status: "review"` and the reply read "All five assets are locked in and saved
to the project… That completes the asset stage. Next up is Stage 6 — shooting
the actual scenes. Want me to move into that now?" — while every save had
returned `locked: false`. (At the time a battery stood between review and lock;
since 2026-09-04 the approval is the lock and the save is one call — but the
lesson is unchanged.) Asked about it directly, the answer was "Good catch — I
skipped that step", which is the shape of the whole class: nothing had refused,
so nothing had to be argued with, and the summary quietly upgraded `review` to
`locked` on the way past. **A tool's own
answer outranks your recollection of it** — `picsart_save_asset` returns
`locked`, `picsart_list_assets` returns `byStatus`, and both exist so this
sentence can be checked instead of composed.

**The override unit is the requirement, not just the stage** — but be exact
about which side of the line a decision sits on, because getting this wrong in
either direction is a failure. The boundary: **choosing HOW to satisfy a rule is
your craft call (which model, which order); choosing to spend or
risk money against a closed gate is never yours.**

So: which model, whether a face needs a crop beside its sheet — craft, decided and recorded, never asked. The lock's evidence is
the approval itself: since 2026-09-05 the save tool locks on `status:
"locked"` with no `stressTest` block, and there is no battery to ask about; a
question to the user about it inverts the
economics (a skip spends *less*, and permission is for spending more). What
genuinely IS the user's: **every charged video dispatch — the quote goes out and
the call waits for the yes, forecast or no forecast (2026-09-07)** — generating
charged video for a scene whose sheets are not approved, exceeding the quoted
plan, and any call that trades quality for money. And the scene check enforces it mechanically: a run dispatches only when
every tag its scenes name has a `locked` row — from the save's own result,
never from a summary.

There is no pipeline board. `picsart_film_tracker` was deleted (2026-08-25):
it was a second place to read a project's state, and it drifted from the real
one — `film.json` — the moment anything moved. Render the map yourself when the
user asks where things stand: a stage list with ✓/✗ gates, then the
scene×assets matrix with its holes marked, both straight from `film.json`. It
is a markdown table, it costs nothing, and it cannot disagree with the state it
was read from.

**Name the stages in the map the way a person would, never by their internal
heading.** `PIPELINE.md`'s titles are filing labels — "Library → gate" tells a
user nothing, and "Bible lock" and "Breakdown" are barely better. Number them as
they are numbered, and title them by what actually happens:

| # | In the map | Not |
|---|---|---|
| 1 | The look, the story, the plan, the shot list | Director's breakdown |
| 2 | (folded into 1 since 2026-09-10 — references are optional inspiration) | Reference gathering |
| 3 | (folded into 1 since 2026-09-10 — the console's Lock is the look's lock) | Bible lock |
| 4 | Cast, places and props | Asset production |
| 5 | (folded into 4 since 2026-09-04 — the approval is the lock) | Library → gate |
| 6 | Shooting the shots | Generation |
| 7 | The cut | Edit |
| 8 | Fixes | Cleanup |
| 9 | Colour | Colour |
| 10 | Sound | Sound |
| 11 | Delivery | Master |

The status column follows the same rule: "waiting on the consistency checks",
not "blocked on locks"; "8 approved, none checked yet", not "8 approved, 0
locked". A stage nobody can name is a stage they cannot tell you is wrong.

Say where the film is at the start of a session — "you are here" — and again at
every milestone.

Three habits that keep the user oriented instead of lost:
- **The checklist is yours, the gate is theirs.** Every gate item is work *you*
  do and track in `film.json`; the user's only call is whether an open gate
  passes. Name what the next stage starts before asking them to open it, and on
  their yes, do exactly that.
- **A stage locator only when the stage actually changes** — one line, and only
  the locator: "📍 Stage 4 · Assets — locking @neferet (2 of 5 done)". With no
  pipeline board this line IS the map, and it stops the "wait, where are we?"
  message. On every other turn it is furniture. Never give it a subtitle
  explaining what the stage means or what the last one cost.
- **Write a board's `note` in plain language.** No pipeline jargon on first
  contact — "nothing renders until the bible is locked" means nothing to someone
  who just typed a logline; "first we plan on paper, then images, then video"
  does.

## The workspace — disk when you have it, the plan block always

When a filesystem is available, create a project directory (ask where, default CWD):

```
<film-slug>/
  film.json            the source of truth — see schema below
  docs/                story.md (the story document — ten lines, then every scene), breakdown.md, the shot list, generation log, DISCLOSURE.md at the end
  assets/              descriptor + reference-image records per asset
  prompts/             final prompt per video, versioned: r1_v3.md
  selects/scNN/        accepted take URLs + their prompts
  edit/  master/       cut records, export URLs, archive manifest
```

`docs/brief.md` is the **living project brief** — the one page a stranger (or a
fresh session) reads to direct this film correctly: logline, the world in three
sentences, the style prefix, who the characters are *as people*, the current
production block, and the standing decisions with their reasons ("hard cuts
only — the film is about abruptness"). Update it whenever a decision would
surprise someone reading only the script; on resume, read it before you say
where things stand. `film.json` holds the state; the brief holds the *intent*.

## The project folder — one film, one tree

**A film is a folder in the user's Drive, provisioned before anything is
generated.** Everything the project makes is persisted into it as it is made, so
nothing ever lives only on a CDN and the whole film is one folder to archive or
hand off. This is the first thing that happens after the idea is agreed — not a
cleanup step at the end:

```
Picsart Films/<film-slug>/
  Library/   locked asset sheets only — what the film is built from
  Assets/    every asset render and every iteration
  Shots/     takes, drafts, finals
  Exports/   masters
```

**Provision it in ONE call**, then never look it up again:

```
picsart_drive action=create_folder
  path: "Picsart Films/<film-slug>"
  children: ["Library", "Assets", "Shots", "Exports"]
```

`result.createdUid` is the project folder and `result.createdUids` maps each
child name to its uid — store all five in `film.json.drive` immediately. Missing
folders are created and existing ones reused, so re-running it after a resume
completes a half-built tree instead of duplicating it. From then on
`picsart_drive action=upload` goes straight into the right uid — never a
browse-`list` first "to find the folder" (each Drive call renders a UI block; a
save should render one receipt, not two stacked browsers). `action=list` is for
when the user actually wants to browse.

**Never show the whole Drive (2026-09-08).** No `action=list` without a
`folderUid`, anywhere in the process — the root listing is the user's entire
Drive painted into the chat, and nothing in a film needs it. The provisioning
receipt renders the project folder itself with its four children (the tool
re-lists the folder it created, never its parent — a project folder's parent
IS the root, and that is how the whole Drive used to appear on day one); every
later Drive call renders the one folder the file landed in. Browsing is the
user's request, never a step.

**Then pass the uid on EVERY generation.** `picsart_generate` and
`picsart_job_status` both take `folderUid`, and it is the only thing that files
a render under its own film:

```
picsart_generate  … folderUid: <film.json.drive.shotsUid>   takes
picsart_generate  … folderUid: <film.json.drive.assetsUid>  asset renders
```

**Without it the render lands in a shared `picsart-genai-mcp` folder at the
Drive root**, where every generation from every project piles up together —
that default exists for generations with no project to belong to, and a film
take is not one of them. This is the one place "provision once, never look it up
again" does not mean "never mention it again": the uid is in `film.json`, so it
costs nothing to read, and a call that omits it saves to the wrong place
silently.

**Video defaults to async, so the `folderUid` must be passed AGAIN to
`picsart_job_status`.** The job carries no memory of it. A take generated with a
folder and collected without one lands at the root anyway, and since takes are
video, that is the call that actually files most of a film.

**Persistence is silent, with exactly one exception.** Saving is your job, not a
topic. Never narrate the plumbing: no CDN hostnames, no "the result has no
`drive` block", no "`saveToDrive` was set but I'm not assuming it persisted", no
live-testing a path in front of the user. Do the save, verify it yourself, and
move on. If a file genuinely did not persist durably, the user hears one sentence
about the *consequence* — "this link expires, so I'm keeping the prompt to
re-make it" — and never the mechanism. A save that worked is worth zero words.

**The exception is the folder itself, said once.** Creating the project folder is
not plumbing — it is a new folder in *their* Drive, holding *their* film, and
where the work lives is theirs to know. So the provisioning call's receipt is
shown and left to speak for itself ("Created "Picsart Films/hold-it" in your
Picsart Drive. It has Library, Assets, Shots and Exports inside — everything
this project makes gets filed in them."), and if anything is added it is one
plain line at most: they can open it any time, and nothing else will be said
about saving again. Never repeat it per asset, per take, or per stage.

**Two destinations, and the difference is not durability — it is curation.**
Persistence happens at *generation*: every render lands in `Assets/` or `Shots/`
as it comes back — **because you passed `folderUid`**, which is what makes that
sentence true rather than aspirational. Promotion happens at *lock*: `picsart_save_asset` with
`status: "locked"` and the project uid files the sheet in `Library/`, which is
the index of what the film is actually built from. Earlier versions stay in
`Assets/`; the library is not a backup, and nothing is ever moved out of the
project.

One exception to plain uploads: **asset sheets save via `picsart_save_asset`,
never a plain upload** — that call writes the registry the library views read; a
sheet uploaded as a plain file is invisible to them (see `film-assets`).

Keep the film state in **`film.json`** — written to the workspace when there is
a disk, and updated whole (never a diff) at every milestone: gate passed, asset
locked, batch generated, review round, picture lock, export.

**Never print it in the conversation.** A screenful of fenced JSON is not a
status report — it is your bookkeeping, and it buries the one line the user
needed under sixty they cannot act on. It is also not how anything gets shown:
the shot list is shown by opening the shot list board, an asset by opening its
sheet, a cut by opening the scene editor. **A request to SEE something is a
request to open its board, never to print its data** — including the second
time, when the board has already been open once this session.

**And generated material arriving is itself the request.** A batch of takes
coming back, a sheet rendering, a retake completing — none of them waits to be
asked about. The board opens in the turn the material lands, because the
alternative is describing pixels the user could have been looking at, and a
description is where a verdict gets quietly self-issued: *"three of the four
check out clean"* is an approval nobody granted, written about frames nobody
saw. Say what you observed, open the board, and let the approval come from
them. Documented failure and the full anatomy in `film-scenes` step 6.

There are exactly two moments the state may appear as JSON in chat:

- **On a surface with no disk, at the end of a working session** — once, so the
  next session can resume. Say what it is in one line ("paste this back to pick
  this film up") and never repeat it mid-flow.
- **When the user asks for it** — "show me the state", "give me the json".

Mid-flow, "where are we?" is answered by the map: the stage list with its gates
and the scene×assets matrix, as markdown tables, in the user's own vocabulary.
Everything a fresh session needs to rebuild the concrete work — every render,
every locked sheet, every take — is in the project's Drive folder already, so
what is lost without a state block is the narrative, not the film.

```json
{
  "film": "cully-hill-boys", "phase": "C", "stage": 6,
  "drive": { "projectUid": "<Picsart Films/cully-hill-boys — provisioned before anything is generated>",
             "libraryUid": "<Library — locked asset sheets land here>",
             "assetsUid": "<Assets — every asset render and iteration>",
             "shotsUid": "<Shots — takes>",
             "exportsUid": "<Exports — masters>" },
  "target": { "duration": 120, "aspectRatio": "16:9", "finalResolution": "1080p" },
  "story": { "who": "Cal, who owes; Tobin, who holds the deed", "where": "the garage, closing time",
             "wants": "Cal: Tobin's signature. Tobin: to keep working", "against": "the buyer comes tomorrow",
             "happens": ["Cal accuses", "Tobin keeps working", "Tobin sets the wrench down"],
             "peak": "Tobin sets the wrench down",
             "ends": "the deed signed on the bench",
             "says": "the thing they nearly split over was never the point",
             "fixed": ["both keep the garage"], "genre": "drama" },
  "shoot": { "ceilingSec": 30, "editModel": "<the picked model's edit sibling>",
             "extendModel": "<the picked model's extend sibling>" },
  "runs": [
    { "id": "r1", "scenes": ["sc01", "sc02"], "shots": ["12A", "12B", "12C", "13A"],
      "genSec": 28, "join": "ends as Cal's hand closes on the wrench; r2 opens on the wrench from close in",
      "cutTimes": [5, 13, 19], "cutTimesActual": [5.2, 12.8, 19.1],
      "refs": ["https://…sc01_12A_start.jpg",
               "https://…view_cal_identity_v1.jpg", "https://…view_cal_wardrobe_v1.jpg",
               "https://…view_tobin_identity_v1.jpg", "https://…view_tobin_wardrobe_v1.jpg",
               "https://…plate_wrench_v1.jpg",
               "https://…sc01_scene.jpg", "https://…garage_day_map.jpg", "https://…sc01_wide.jpg"],
      "refRoles": ["START FRAME", "IDENTITY @cal", "WARDROBE @cal", "IDENTITY @tobin", "WARDROBE @tobin",
                   "PROP the wrench", "SCENE PICTURE", "SPACE map", "SPACE wide"],
      "promptVersion": "v3", "takes": 2, "editPasses": 1, "extends": 1,
      "pieces": [
        { "url": "https://…run-r1-take2-edit1.mp4", "from": 0, "to": 28, "promptVersion": "v3",
          "call": "edit", "note": "cut 3: mug is white ceramic" },
        { "url": "https://…run-r1-extend1.mp4", "from": 28, "to": 34, "promptVersion": "v4",
          "call": "extend", "note": "13A holds two more seconds on Tobin" }
      ],
      "select": { "url": "https://…", "jobId": "job_…" } }
  ],
  "model": { "id": "<the video model the user picked on picsart_model_choice — a version id, recorded once>",
             "draftResolution": "480p",
             "pictures": { "people": "<live 4K-capable scene-image model id selected at Stage 4>",
                           "map": "<newest GPT Image id>", "vintage": "<newest GPT Image id>",
                           "modern": "<newest Nano Banana id>" } },
  "banDictionary": { "dark": "low key", "jolting": "rapid motion" },
  "gates": { "1": "passed", "2": "passed", "3": "passed", "4": "passed",
             "5": "passed", "6": null },
  "voicePolicy": "native",
  "blocked_on": null, "attempts": {},
  "bible": { "stylePrefix": "<verbatim — pasted into every prompt of this world>",
             "palette": { "dominant": "amber lamplight on old plaster",
                          "secondary": "burnt-orange rust and oiled timber",
                          "accent": "lime-green enamel",
                          "accentBelongsTo": "the brothers' car" },
             "setup": { "genre": "drama", "tone": "auto", "palette": "auto",
                        "scheme": "analogous", "hue": "amber", "hueDegrees": 60,
                        "ratio": "169", "camera": "35mm", "lens": "ana" } },
  "assets": [
    { "tag": "@cal", "type": "character", "version": "v1", "status": "locked",
      "descriptor": "<verbatim — the sheet's prompt and this record; never pasted into a video prompt>",
      "portrait": "https://…portrait_cal_v1.jpg", "profile": "https://…profile_cal_v1.jpg",
      "sheet": "https://…sheet_cal_v1.jpg",
      "grid": "T-cut triptych at the model's top resolution: left column two stacked face panels, right tall full-body panel; a states row below",
      "panels": ["1 front face (top-left)", "2 side face (bottom-left)", "3 full body front (right tall)",
                 "4 @cal_wet (states row)"],
      "views": [
        { "id": "identity", "source": "portrait, head to neck", "box": [512, 0, 1024, 1536], "delivered": [1024, 1536],
          "url": "https://…view_cal_identity_v1.jpg", "bytes": 291204 },
        { "id": "profile", "source": "profile, head to neck", "box": [512, 0, 1024, 1536], "delivered": [1024, 1536],
          "url": "https://…view_cal_profile_v1.jpg", "bytes": 240310 },
        { "id": "wardrobe", "panel": "3 full body front", "position": "right tall",
          "box": [1024, 0, 1024, 2048], "delivered": [1024, 2048],
          "url": "https://…view_cal_wardrobe_v1.jpg", "bytes": 188440 },
        { "id": "wet", "panel": "4 @cal_wet", "position": "states row",
          "box": [0, 2048, 1024, 1024], "delivered": [1024, 1024],
          "url": "https://…view_cal_wet_v1.jpg", "bytes": 164880 }],
      "variants": ["@cal_wet"], "scenes": ["sc01", "sc02"],
      "blocking": { "asOf": "sc01",
                    "left": "on foot, frame-right, walking away from the bench",
                    "exit": "frame-right" },
      "voice": "low, unhurried, faint valley accent" },
    { "tag": "@maya", "type": "character", "source": "user", "version": "v1", "status": "locked",
      "original": "https://…maya_portrait.jpg",
      "sheet": "https://…sheet_maya_v1.jpg",
      "grid": "2046×1024; 1 row × 3 equal cols",
      "panels": ["profile (leftmost)", "back (centre)", "@maya_coat_off (rightmost)"],
      "views": [
        { "id": "coat_off", "panel": "@maya_coat_off", "position": "panel 3 of 3, rightmost",
          "box": [1364, 0, 682, 1024], "url": "https://…view_maya_coatoff_v1.jpg", "bytes": 64200 }],
      "derived": [{ "url": "https://…maya_portrait_q85.jpg", "op": "jpeg q85 — run budget" }],
      "descriptor": "<minimal — written from the photo; costume only where a scene departs from it>",
      "scenes": ["sc02"] },
    { "tag": "@garage", "type": "location", "version": "v1", "status": "locked",
      "descriptor": "<verbatim — architecture, materials, the anchor object, one light, the palette line>",
      "views": [
        { "id": "map", "url": "https://…garage_day_map.jpg", "madeFrom": "sc01.pictures.wide",
          "sides": { "A": "shelving wall", "B": "roller door", "C": "bench wall", "D": "tarp wall" } },
        { "id": "wide", "url": "https://…sc01_wide.jpg" }],
      "scenes": ["sc01", "sc02"] }
  ],
  "scenes": [
    { "id": "sc01", "slug": "EXT. GARAGE — DAY", "status": "selects-complete",
      "locationMap": "<verbatim spatial map, pasted into every prompt of the scene — with the room map's sides: Side A the shelving wall, …>",
      "pictures": { "scene": "https://…sc01_scene.jpg", "wide": "https://…sc01_wide.jpg",
                    "wrecked": [{ "angle": "WS", "fromRun": "r1", "at": 27.4, "url": "https://…r1_f27.4.png" }] },
      "shots": [
        { "id": "12A", "status": "final", "duration": 5, "run": "r1",
          "refs": [1, 2, 3, 4, 5, 6, 7, 8, 9],
          "design": { "move": "slow push-in", "size": "MS", "fov": "40°",
                      "aperture": "f/4", "lighting": "window",
                      "tailHold": 1.2, "slowReason": "emotional pause",
                      "acting": { "@cal": "anger·2, hides fear, gaze on Tobin" } },
          "picture": "scene",
          "trim": { "in": 0.6, "out": 5.1 }, "trimSource": "user" }
      ] }
  ],
  "review": { "round": 1, "lastFeedbackSummary": "…" },
  "credits": { "spentEstimate": 320, "nextActionQuote": 45 }
}
```

Why the awkward fields: `descriptor` and `stylePrefix` are **verbatim** because
consistency dies the day someone shortens them; `bible.setup` holds the console's
**preset ids** (what the user chose) while `bible.palette` holds the *resolved*
colours — the scheme and hue written as colours plus what carries them
(`PRESETS-LOOKS.md`, *Colour*), absent when the scheme is Auto — plus
`accentBelongsTo` — the one thing that owns the accent, which the
console does not ask and you decide while designing the assets; `trim` is an
**edit-time fact recorded on the shot** — a keep-window in source seconds, and
for a shot inside a run the source is its **piece** — the run's file, or the
regenerated segment that replaced part of it (`runs[].pieces`, 2026-09-04) — so
the window is in that file's seconds (12B's is `{in: 5.4, out: 12.6}` on the run
file, not `{0.2, 7.4}`): every shot of a piece is a clip in the scene sharing one
url with a different window, which is how the scene editor and Final Cut
already work — so a
trim does not lose itself between the scene editor and the assembly, and
**`trimSource` says where the window came from** — and since 2026-09-04 the
only value written is `user` (they moved it in the scene editor): the automatic
rough cut that produced `aligned` (word timings) and `planned` (beat clock) is
off, because a run arrives already cut by the model. Read either of those on an
old `film.json` as an automatic window from before the switch and leave it alone
exactly as a `user` one is left alone — nothing automatic touches a window, and
recomputing over somebody's own edit is how a helpful default becomes an argument
(`virtualMove` sat beside it until 2026-08-31, holding a free crop-path move
added at the edit; crop-path is gone and a camera change is a retake now, so the
key is retired — read one on an old `film.json` as "nothing to apply");
`shoot.ceilingSec` is
the model's live max duration, read from `picsart_model_params` once at the model
pick so the run pass has a number to close runs against, and `shoot.editModel` /
`shoot.extendModel` are the siblings an edit or an extend call goes to
(`film-scenes` step 5); `runs[]` is top-level because a
run spans scenes (2026-09-03: as much of the film as fits the ceiling is one
multi-shot generation, and there are no per-shot exceptions) and it is what
actually got bought: which shots, the planned `cutTimes` and the
`cutTimesActual` the delivered take was split at (the model cuts near the plan,
not on it, and every trim downstream hangs off the real numbers), `refs` — the
ordered images the call carried in `imageUrls`, with `refRoles` saying what
the tagging header told the model each one is (a face, an outfit, `START FRAME`,
`FRAME REFERENCE for shot N`, `SPACE` — the `startFrame` field stays empty and
the role travels in words), which every shot block numbers against, so a shot's
`refs` are indices into it — one `promptVersion`, `takes`
and `editPasses` per run, and one `select`; each shot points back with `run`,
keeps its composed `still` when one was requested (an animatic panel if one
was watched, and in `refs` its labelled `START FRAME` or `FRAME REFERENCE`); `locationMap` lives on the scene
because it is written once and pasted into every prompt unchanged; `design` stores
preset names, not prose, so a change is one field and the recompiled prompt differs
in exactly one block. A shot over 8 s carries its reason in `design.durationReason`, one line.
**`blocking` is the one piece of character state that
crosses a scene boundary** — where they were left and which side of frame they
went out — because `locationMap` fixes geography only *within* a scene and
nothing else carries a heading out of one: a character who exits frame-right and
enters the next scene frame-right has quietly broken the geography, and it is
the continuity error viewers feel without being able to name. Note what it is
NOT: wardrobe, damage and wetness are **not** recorded here, because a state
change is a separate asset with its own tag (rule 2) and a field holding
"now soaked" would compete with `@cal_wet` for the truth. `stressTest` is not required (2026-09-05): the save tool locks on the
review-board approval, and no row carries a battery — there is none in this
flow. Statuses: assets
`draft → review → locked`; shots
`planned → drafting → iterating → final`; a shot regenerated after picture lock gets
a new `promptVersion`, never an edit in place. **`story` is the ten lines the user said yes to, and `docs/story.md` holds
every scene as what we see and hear** (2026-09-09,
`film-development/references/DRAMATURGY.md`); `runs[]` is seeded by the Stage 1
video plan (`id`, `scenes`, `genSec`, `join`) and filled in Stage 6; the scene
contracts of 2026-09-05 are gone. The shotlist board
owns a card's action, line, tags, values and order; `design`, `refs`, `run` and
`trim` stay on the shot by id (`references/WIDGET-FEEDBACK.md`).

## Ten rules that never bend

1. **Generate nothing for the film until its scene's assets are locked.** The
   scene×assets matrix — read off `film.json` — is the gate, and a hole is a
   stop.
2. **One asset — one approved sheet; one reference image — one view.** Each
   character, location, and prop has a single approved sheet (descriptor + one
   image). The descriptor is the sheet's prompt and the asset's record; since
   2026-09-09 it is **never pasted into a video prompt** — the pictures carry
   identity and the prompt names them by number
   (`film-scenes/references/PROMPT-BLOCKS.md`, the one home of the prompt's
   shape). A state
   change (wet, bloodied, new costume) is a **panel on that sheet with its own
   tag** (`@cal_wet`; since 2026-09-04 a panel, not a second sheet) — a tag,
   never a note in the shot prompt. What reaches the model is not the sheet:
   since 2026-09-07 each panel the film uses is **cut out as its own image**,
   and a run's `imageUrls` carries those views, one thing per image. A
   multi-panel picture with the right panel named in words is what put the
   wrong Alina in sc04a.
3. **Edits are surgical.** One preset, one line, one block at a time. A rewritten
   prompt loses whatever was working.
4. **Show promptly; inspect before use.** A finished image may be shown
   immediately while its visual check is performed with `picsart_view_image`.
   Explain any defect under it. Approval, reference use, cropping and dependent
   generations wait for the check to pass. For `gcdn.picsart.com` images, build
   a preview URL by preserving the path and existing query parameters and
   setting `to=min`, `r=1280`, `type=jpeg`; use `r=2048` to read face panels.
   Inspect the returned pixels, not the URL. If that host transformation is
   unavailable or still too large, export a small JPEG with
   `picsart_media_export` and inspect it. Previews are for viewing only:
   reference inputs, crops and start frames keep the full-resolution original.
   Video checks use extracted frames. A tool error means the check has not
   happened; use the documented fallback or explain the block.
5. **Everything is logged, lightly.** One prompt per video, the take's link and
   the verdict. Without that a good shot cannot be reproduced.
6. **A dropped call is never a failed generation.** "Server isn't responding" is
   a *response* failure: the render usually completed, charged, and (with
   `saveToDrive`) landed in Drive — a blind retry pays twice for an image
   already owned, and writes finished assets off as "blocked". So every charged
   `picsart_generate` in this pipeline — sheets, stills, video — passes
   `async: true` (a handle always exists to poll),
   and recovery from any drop is **check first, never resubmit**:
   `picsart_job_status` on the handle, or list recent Drive files for a sync
   call that slipped through.
7. **Nobody is in a video without their picture (2026-09-09).** Every video
   call — a 30-second run, a five-second bridge, an insert, a one-shot reshoot,
   whatever the clip is called — carries in `imageUrls` the identity view and
   the wardrobe view of every person who appears in it (the two pictures the
   lock step cuts from the sheet, `film-assets`), and the scene's
   pictures. A person carried by words alone is drawn from the model's habits
   (a wasteland in words puts a long coat on her), so words-only is a failed
   take before it is sent. It follows that the `startFrame` field is never
   filled, on any clip: an exact frame shuts every reference out (verified
   2026-08-26), and a frame shows only the people in it. The frame a video
   must open on is picture 1 in `imageUrls`, labelled `START FRAME` in the
   tagging header, beside the others. On 2026-09-08 a five-second bridge went
   out with scene 1's last frame in the `startFrame` field — one woman's face
   — and no picture of the other woman, who was in the shot but not in the
   frame; she came back in a long coat with two flasks, and the bridge put a
   wardrobe jump on both of its joins. One habit rides with this rule: every
   object is named in one place with one position — a flask *on her strap*
   and *in her hand* in the same prompt is two flasks. Three cases the rule
   settles (2026-09-10): **a person the user photographed** — the photograph
   is the identity view and, when it shows the outfit the story dresses them
   in, the wardrobe view too; when it does not, ask for one more photograph
   that does, and never generate a lookalike to fill the slot. **A state** —
   when a dependent run takes frames from an earlier run because a person
   changed (dusty, torn, wet), the frame that shows it becomes that person's
   state view and **replaces** their wardrobe view in the dependent run; a
   clean coat beside a torn one makes the model average them. **A prop** —
   every object the film gave a plate to rides beside the people as `IMAGE n —
   PROP: <name>` and is named by that number where it appears; a canteen
   described in words came back as a flat flask on 2026-09-08.

8. **Default review carries the whole film
   (2026-09-09).** Every `picsart_scene_editor` call opens on a `scene` that
   plays every accepted take plus the new one, in playback order — the montage
   built by `../film-edit/references/ASSEMBLY.md`. A film with two videos and a
   third just delivered opens as a scene of **three**, the new one in its story
   position — never a scene holding only what was just generated. `videoUrl` is
   for a film that genuinely has one video. **Why:** the
   only question worth asking about a new take is whether it *joins* — the seam
   into it and the seam out of it are the thing that fails, and a clip played on
   its own hides exactly that. A take that looks right alone and jumps in the
   film is the normal case, not the rare one, and it was being approved because
   nobody saw the join. The rule that permitted this said *one board per
   dispatched batch*, and a batch of one clip is a board of one clip. Watching
   the whole film costs nothing: the scene is windows over the source clips, and
   the editor plays it in the browser — it never renders and it never exports. **Focused join diagnosis/comparison is
   the exception** (`../film-edit/references/JOIN-REPAIR.md`): review both sides
   of the boundary, including a proposed insert, as a labelled preview separate
   from the accepted cut. Never approve a bridge from watching it alone.
9. **The join between two videos is an angle change; a copied frame is the
   exception and must be named (2026-09-09).** This is the default for planning
   new runs, not an automatic repair for existing clips. User-requested join
   repairs follow `../film-edit/references/JOIN-REPAIR.md`, including motivated
   inserts and reference choices based on inspected boundary states. Two videos meet the handbook's
   way — the camera changes, the action carries, no pixels copied — which is what
   lets every video of the film dispatch in parallel. Taking video A's last frame
   into video B is permitted **only** when a physical state exists in A's pixels
   and in none of the approved pictures (a torn coat, a wrecked room, dust on a
   face); before dispatch, that state is named out loud in one sentence, and if
   the sentence names nothing, there is no exception and the angle change carries
   the join. The `startFrame` *field* stays empty either way (rule 7) — a frame
   travels as picture 1 of `imageUrls`, labelled `START FRAME`. **Why:** a copied
   frame makes B wait for A, and it carries only the composition and the people
   visible in it — everyone else regenerates from words and comes back changed
   (the 2026-09-08 bridge). The angle change hides the seam without paying that.

10. **No place is in a video without its picture, and the picture has to cover
    what the frame will show (2026-09-10).** Rule 7 protects the people; this is
    the same law for the place, because it was the half that had no rule. Every
    video and every edit set in a place carries that place's **map** — the
    geometry — and carries the **wide** as well whenever the framing opens up
    beyond a tight frame. The budget ladder may drop the wide only when the map
    is present *and* no cut pulls back; **the map is never dropped**, exactly as
    the identity and wardrobe views are never dropped. An edit inherits the
    run's reference list verbatim, so a hole in the run becomes a hole in the
    edit — and an edit that reveals more of the setting than the clip showed
    must **add** the map and the wide, because the run's list only anchors what
    was already visible. **Why:** a tight frame constrains a doorway and nothing
    else; ask for a pull-back and the skyline, the street and the far wall are
    drawn from the model's habits, so the place changes while every face stays
    right. On 2026-09-10 clip 3 went out with only a tight frame of its setting,
    its edit inherited the same list, the edit pulled back, and the revealed
    skyline was somewhere else.

## When a tool or endpoint is down

Outages happen (a connector drops i2i while t2i works, a widget tool is
missing from the session). They are **status, not decisions**: report the
outage in one line, keep the conveyor moving on everything free and working
(reviews, descriptors, checklists, planning), park the blocked items in
`openDecisions` with a retry note, and retry quietly next turn. Do **not**
turn an outage into a user choice when the pipeline's own rules already answer
it — "redraw the lead's variant from scratch, or wait for i2i?" is not a
question, because taking consistency debt on a face to dodge a temporary
outage is exactly what the rules forbid. Offer a genuine workaround decision
only when both paths are actually defensible.

The formalization that kills thrash loops (a documented forensic case: 14
retries, six silent minutes, a fabricated diagnosis):

- **Running out of credits gets ZERO retries and opens the purchase page.** An
  explicit insufficient-credits or insufficient-balance response from any
  Picsart tool is not a transient failure. Do not resubmit it, switch models, or
  substitute another charged operation. Preserve `film.json`, its job handles,
  and the current scene or asset state, and set `blocked_on` to
  `Picsart credits for <the paused operation>`.

  Open **https://picsart.com/pricing/** in the user's default browser so they can
  subscribe or buy more credits. In a shell-enabled desktop host, use its native
  URL opener (`open` on macOS, `xdg-open` on Linux, or `Start-Process` on Windows),
  not a web fetch: the user needs the interactive, signed-in page. If the host
  cannot launch a browser, give them a clickable
  [Buy more Picsart credits](https://picsart.com/pricing/) link instead. Say which
  operation is paused and continue only after the user says their credits are
  available; never poll their balance or retry automatically.
- **Every failure has either a NAMED substitute or none.** Named substitutes
  are sanctioned in the skills (t2i when i2i is down is NOT one for a lead's
  variant; the markdown fallback when a board tool is absent IS one; the
  by-ear path when transcription is absent IS one). If no substitute is
  named, there isn't one — do not invent a workaround chain to keep moving.
- **Other critical-component failures retry ONCE, then stop and hand it back.** The tools
  the pipeline cannot run without (`picsart_generate`, the job status poll,
  the export) get one identical retry; a second failure is the user's news,
  in one plain line: what you tried to do, what blocked it, what you need.
  Never reword-and-refire, never sleep-and-loop, never diagnose beyond what
  the error actually says.
- **A 401 gets ZERO retries, and it is almost never the user's login.**
  `User token was not provided or is invalid` is emitted in exactly one place —
  the MCP server's own auth guard — and despite its wording it does not check a
  token at all. It checks whether the request carries the `user-id` header the
  Picsart gateway is supposed to inject. So the call was refused *before it was
  sent*: nothing reached Picsart, nothing was charged, no job exists to recover,
  and retrying an identical payload can only produce an identical 401.

  Report it as what it is — **identity did not reach the server on that call** —
  and say plainly that it is a gateway or deployment problem rather than
  something they can fix by logging in. Three things NOT to say: that Picsart
  rejected it (it never saw it); that their session expired (there is no user
  token in this path — upstream calls are authorized by a server-side service
  token); and that visible credits prove anything (`picsart_credits` does not
  use that token at all — it reads an internal credit service with only
  `x-act-as-user-id`, which is exactly why it can keep working while every
  generation fails).

  When it happens, the one useful probe is a tool that needs identity but is not
  on the guard's list, such as `picsart_job_status`: it fails with a *different*
  message, `Missing 'user-id' header…`, which confirms the same root cause in
  plainer words and tells the user which layer to look at.
- **Track it in `film.json`**: `blocked_on` names the ONE missing thing
  (or `none`), and `attempts` counts per tool+failure so the once-only retry
  is enforced by bookkeeping, not memory.

## Model policy

The pipeline is model-agnostic; the current fit is checked, not remembered.
**The video model is chosen in Stage 1, right after the story and before the
video plan is written** (2026-09-03; it used to sit at the bible lock): the shot
list needs its duration ladder, the run pass needs its ceiling, the reference
budget is its `imageUrls` max, and the edit sibling is its repair path — nothing
downstream of the console can be planned model-blind. Read
`picsart_model_catalog` (`mode: "video"`, **`purpose: "generate"`**) and
`picsart_model_params` live. Pass `purpose` every
time: `mode` and `inputType` say which media go in and out, never what a model is
FOR, so without it the shortlist arrives salted with upscalers, reframes,
video-extend and preset-effect models. `edit` is for changing a clip you already
have; `utility` is upscale/reframe/lip-sync/preset — ask for it by name when that
is genuinely the question. What a film needs from its one video model: **native
multi-shot** — a declared cut count and cut times honoured inside one generation,
at the finish resolution, for the run lengths the shot list needs (since
2026-09-03 the unit of generation is the run — as much of the film as fits the
ceiling, scenes included; `film-scenes` has the finding and the rules) — plus
**reference images in** (`imageUrls`, enough slots for every view, plate and
approved still the run uses — views, never sheets — each named by index and role in the prompt —
the start frame travels as one of them, labelled `START FRAME`, since the
`startFrame` field would lock the references out), native audio with
lip-sync for dialogue, and **edit and extend siblings** (`purpose: "edit"`, v2v
— the edit one takes the clip + a change + the same references, the extend one
takes the clip + what happens next) so a fix or a longer shot never re-rolls the
run (`film-scenes` step 5). The multi-shot line is a hard filter, not a preference: a model that
caps resolution on long takes, or will not hold a cut list, is a model that
shoots the film shot by shot, which is the measured-worse path. Check the
ceiling, the `imageUrls` max and the resolution enum in `picsart_model_params`
before the board opens. Put the shortlist through `picsart_model_choice` and let the user pick —
the same habit throughout. One model for the whole film unless a
world-switch demands otherwise.

**The choice board is the only model UI this pipeline opens, and it is short.**
Never `picsart_list_models`: that widget is the catalogue shelf — every match,
unranked — and opening a card there puts the user in front of a full parameter
form with its own generate button, a second production flow running beside this
one and spending credits outside the plan. What the board carries instead is a
decision already made: **your recommendation plus the one or two alternatives
whose trade-off a director could genuinely prefer** (three cards is the target,
four the ceiling), exactly one badged `recommended`, each `bestFor` written as
the trade — "one continuous 30s take, but tops out at 720p" — not as a spec
sheet. Narrowing eight candidates to three is your job, not theirs; a long board
is the catalogue handed back with extra steps.

The same rule binds **image** models for asset work: the film's target format
is a hard constraint. Filter the catalog by `supportedAspectRatios` containing
the film's aspect ratio *before* comparing anything else — never settle for a
model whose ratio enum lacks the finish format and let it "cap" the stills at
the nearest ratio it has.

**Image models are families, not ids (2026-09-10).** The docs name the family
the handbook uses for each job; the id is read off `picsart_model_catalog`
once, at Stage 4's first call, and recorded in `film.json.model.pictures` so
one film never changes model midway while the next film picks up a newer
version the day it lands (`references/PIPELINE.md`, Stage 4, *Image models
are families*):

- **`people`** — the 4K wide's model, selected from the live catalog and
  schema; Nano Banana is the current repo default. Seedream Pro is an
  alternate after identity failures, with any smaller crop disclosed;
- **`map`** — the room map, and a prop's plate: the GPT Image family
  (`gpt-image-*`; where a version ships in tiers, the fast one);
- **`vintage`** — the era tones' portrait and sheet: the GPT Image family (the
  premium tier where there is one — a face is cut from it);
- **`modern`** — the modern path and every profile: Nano Banana (Google's
  `gemini-*-flash-image`, never the `-lite`);
- a point fix on a sheet goes to an image **edit** model (`purpose: "edit"`,
  the newest that takes a mask); signs, letters and labels go to the model that
  can spell, as a separate task, never generated inside a shot.

A model id written anywhere in these docs is evidence with a date, not the
rule. One video model for the whole film still stands, chosen by the user on
`picsart_model_choice` and recorded with its edit and extend siblings.

## Stage details, checklists, and mistakes

`references/PIPELINE.md` holds all 11 stage cards: goal, input, output, exit
checklist, and the documented common mistakes. Load it when opening a stage or
judging a gate. `references/PRESETS-LOOKS.md` holds the film-setup vocabulary
(genres, tones, the colour scheme and hue, the frame, the camera and the lens
family) — lighting, focal length, aperture, palettes, shot length, camera
movement and framing are deliberately not setup decisions, because none of them
can honestly be decided once per film; they are per-shot calls (the palette
table lives on there as the shot's colour). Grain is
not a control either (2026-09-07): it is a property of the stock and rides in
the Camera preset's fragment. A **tone** is an era recipe (2026-09-07): it sets the
camera and lens to a named period of cinema and adds one clause of its own,
while colour remains independent,
so when the story names a period or a cinema tradition, pass its id as
`suggested.selections.tone` and let the console expand it.
`references/WIDGET-FEEDBACK.md` documents every film board's feedback payload.

## Cost ledger

| Free | Charged, no rollback |
|---|---|
| all planning, breakdown, prompts, presets, `picsart_model_catalog`, `picsart_model_params`, `picsart_preflight`, `picsart_media_validate_scene`, authoring and re-authoring the cut, **splitting a run at its cuts** (probe + bootstrap + validate; the scene editor plays the built scene, so it never exports), every board and console (`picsart_film_setup`, `picsart_shotlist_board`, `picsart_asset_review`, `picsart_scene_editor` — which renders nothing, `picsart_model_choice`), every `picsart_media_contact_sheet` and `picsart_media_export` (real GPU renders, so it costs time and leaves a file — **not credits**, 2026-09-07), and `picsart_final_cut` **including its render** — that one happens in the user's browser, so editing and exporting there are both free | every `picsart_generate` (images and video) |

## Traps

- Starting generation "just for one scene" before its assets pass the gate. That is
  the trap the whole pipeline exists to prevent.
- Dispatching video on the forecast. A forecast is a plan; the purchase needs a
  preflight number in front of the user and a yes after it — per dispatch, at
  any pace (`film-scenes`).
- Shortening a descriptor "for brevity" inside a prompt. Consistency dies right there.
- Building a state variant as its own asset with its own approval. Since
  2026-09-04 `@cal_wet` is a panel on @cal's sheet, and since 2026-09-07 a
  view cut from it and numbered in the references header like any other image — a separate
  *sheet* only when the change lasts most of the film.
- Handing the video model a multi-panel sheet, or a composite the user
  supplied, with the wanted panel named in words. Cut it into views at lock.
  No pointer works: sc04a and sc04b ran the same correct prompt and returned
  two different women, one frame blending two panels into a face not in the
  cast. A prompt that names a panel is a dispatch error (`film-assets`).
- Letting the registry go stale — two weeks later it lies, and consistency rests on
  one person's memory.
- Losing the project to an expired CDN link because nothing was saved to Drive
  — or, for a locked asset, recording the export's URL instead of the one
  `picsart_save_asset` handed back: the save re-hosts, the export URL expires
  (`film-assets`, step 4).
- Altering a user's own photo on the way in, or asking them to approve it on a
  board. Since 2026-09-04 the original is saved byte-for-byte (probe-matched)
  and is IMAGE 1 in every run; changed copies are derived files with the
  reason recorded, and a sheet is generated *from* the photo only when a card
  needs an angle it lacks (`film-assets`).

Referenced files: 4

film-assets31.8 KB

View saved version →

---
name: film-assets
description: "Produce and lock film assets: one T-cut sheet per generated character, the portrait/profile/sheet route for a supplied photograph, prop plates, and individual reviews as assets arrive. Cut approved sheets into single-view references for recurring identity. Use for stages 4–5 after the shot list, before film-scenes; preserve user photographs and approve each asset before using it."
---

# ChatGPT and Codex integration

Apply these host conventions throughout this skill and its references.

- Discover the connected Picsart tools and use their actual names and schemas.
  The `picsart_*` names in the workflow identify tools, not a fixed host namespace.
  If the connection is missing, explain that Picsart must be connected; continue
  script and shot planning when possible. Never invent tool results or renders.
- Read board decisions from the current user message and attached widget context.
  Use `read_widget_context` only if the host actually exposes that tool. Do not
  assume ChatGPT has Claude's context reader. Treat a board's initial tool result
  and unsent widget state as previews, never as user approval. If a submitted
  decision has no accessible payload, explain briefly and use the text fallback
  to collect the missing decision; do not guess or repeat charged operations.
- Use the host's available question interface or one concise conversational
  question for preset choices.
- UI rendering and delivery of feedback are the remote MCP server's responsibility.
  If a board is unavailable, present its relevant options in text and record the
  user's selection using the same documented feedback fields. Do not claim that
  a plugin manifest makes Claude-specific widgets compatible with ChatGPT.
- Save `film.json` and artifacts in the user's writable project workspace, not
  the installed plugin directory. If persistent file tools are unavailable,
  keep a structured project checkpoint in the conversation and provide it for
  download when the host supports files. Never claim a file was saved without
  confirmation from a file tool.
- Honor the user's current instructions and already authorized scope. Preserve
  quoted spend limits and production locks; do not infer authorization from a
  preview, a timeout, or a connection being installed. A timed-out charged call
  needs a status check before considering a retry.
- This package holds six film skills (`film`, `film-development`, `film-assets`, `film-scenes`, `film-edit`, `film-finishing`) and three motion skills (`motion`, `motion-import`, `motion-design`). Route between them directly.
- `motion-import` reads a design's layers through the Figma app's tools. If Figma is not connected in this host, use its flat-frame fallback and say so once.


# Assets — casting, location scouting, and the prop shop, on images

Before preparing image prompts or scheduling work, read
[ASSET-EFFICIENCY.md](references/ASSET-EFFICIENCY.md): task-scoped reference
styling, per-asset readiness and content-based dependency checks. These qualify
the scheduling and review rules below without weakening approvals.

**One sheet per generated character; review each ready asset.** Compile one
T-cut sheet with `picsart_film_compile_asset_prompt`, `kind: "sheet"`, a face
in `faceAndOutfit` and/or all six `identity` fields, and the outfit by item.
Generate it from the descriptor without uploaded pictures. The front face,
profile and full-body outfit share one image. Use a live-schema-confirmed 4K
image model and probe the result: the identity panel must clear 1024 px.

The portrait → profile → sheet chain remains the route for a supplied real
photograph. Preserve that photo and check dependent images against it.
Pass the film's locked camera and colour choices to the compiler alongside
`tone`: `tone: "auto"` alone supplies no rendering style. If the look is still
entirely Auto, resolve its camera on the setup console before compiling. A
compiler error is a request to fix the input or report the block, not a reason
to switch a generated character to the photograph chain.

Quote every ready independent asset together, then dispatch their approved
`picsart_generate` calls asynchronously in one batch. Show each finished
asset immediately and open `picsart_asset_review` for the ready subset;
unrelated renders and retries do not hold it. Approval plus a completed
visual check locks that asset. Scene work may start when that scene's own
cast and props are ready.

At lock, cut each sheet into one image per view. Videos receive the approved
full-resolution views, never the multi-panel sheet. The sheet remains the
archive. See `references/ASSET-SHEETS.md` for grids, descriptors and cropping.

**An asset is still a pair: text + image.** The descriptor is the sheet's prompt
and the asset's record, and it is never pasted into a video prompt (2026-09-09,
`../film-scenes/references/PROMPT-BLOCKS.md`); its **views** travel in
`imageUrls`, numbered and labelled to its shots, and the pictures are the
consistency mechanism. Say the
stage to the user in one sentence the first time — *"one picture per character
and place; approve them and we shoot"* — never through the vocabulary of these
docs.

## Every person and prop gets its pictures (2026-09-09 — no tiers)

- **A character**: one generated T-cut sheet; for a supplied photograph, its profile and sheet
  (`references/ASSET-SHEETS.md`). **Locations are not generated here since
  2026-09-08**: every place gets its descriptor at step 1 and its pictures —
  scene picture, wide, a room map when the scene needs one — from
  the scene in `film-scenes` step 3, the handbook's order
  (`references/ASSET-SHEETS.md`, *The location*).
- **A prop**: one plate, judged on the same board, and carried into every video
  the prop appears in as `IMAGE n — PROP: <name>` (rule 7, 2026-09-10) — a
  plate nobody attaches was paid for nothing, and words alone drew a round
  canteen as a flat flask.
- A background figure or an object nobody handles has no row; it is two or
  three descriptor lines inside the shot that shows it.

**State and scenario variants are panels on the sheet, and views in the run.**
`@cal_wet`, `@cal_armed` — each is a panel of a states
sheet, generated from the portrait and the approved sheet so it is the same
person, then **cut out as its own image** and numbered in the references header like any other reference (*"@cal_wet — IMAGE
3"*). The tag resolves to a file, not to a region of a picture: a panel pointer
inside a multi-panel image is what sc04a got wrong. A variant gets its own
*sheet* only when it is a different picture for most of the film — a costume
change that lasts an act, an injury that rewrites the face — and that is a craft
call, not a rule. A place at night is not a state here at all: it is a scene of
its own with its own pictures (`film-scenes` step 3).

## When the user brings the asset — the photo is the identity (2026-09-04)

A photo of a real person, a real product, a real place is not a reference for
a sheet; it **is** the asset, and it beats any generated image at the one thing
this stage exists for. No generative model holds a face at 100% — the only
100% face is the photograph — so the flow keeps the photograph in the video
model's hands and generates *around* it, never instead of it.

1. **Save it exactly.** The upload URL is temporary; the durable copy goes to
   the project's Drive folder as the bytes the user sent — `picsart_drive
   action=upload` with the upload URL, the original name kept; read
   `durability` in the result. Probe both (`picsart_media_probe_media`, free):
   `bytes`, `width`, `height`, `contentType` must match, or the save is not a
   copy. No re-encode, no crop, no enhance, no cutout on the way in. That file
   is `assets[].original`, and it is the identity source of every run the
   asset is in. This happens the moment the photo arrives (Stage 1); the
   registry row is written at Stage 4, step 4, once the cards are known.
2. **Identity or inspiration — decide by looking, never by asking**
   (`picsart_view_image`). A person, product or place the film is about is
   identity: this section. A film still, an artwork, a mood photo is
   inspiration: a reference caption, and it never enters `imageUrls` — a still's
   actors would join the cast. One short question only when an image genuinely
   reads both ways.
   **Then a second question of the same picture: one view, or a composite?**
   People hand over one file with four pictures in it — life stages, outfit
   options, a phone contact sheet. A composite is still the asset and is still
   saved byte-for-byte, but it **never enters `imageUrls` whole**: read the
   panels off the pixels, cut each view the cards need from a *copy*, and
   record them in `derived[]` with the position in the `op` and in `views[]`
   like any other view (`references/ASSET-SHEETS.md`, *A composite the user
   supplied is cut before it is used*). A composite passed whole with its panel
   named in words is exactly the sc04a failure.
3. **Generate an angle sheet only when the cards need one — and ask for photos
   first (2026-09-07).** Count what was given against what the cards need:
   every angle in hand, nothing is generated. One or two views and a card asks
   for an angle or a state the photo does not have — profile, back, soaked, a
   different coat — ask for those photos first, and offer the generation as
   the alternative, with its price. When it is generated, it is the handbook's chain **from the photo** — the
   profile (the compiler writes it), then the T-cut sheet from
   photo, profile and outfit — with the photograph in the portrait's place and
   the four photo standards checked first (`references/ASSET-SHEETS.md`, *The
   user's photograph*), in the same batch as the generated sheets. It goes on the review board like any sheet,
   `judge`: *"the same person as the photo — face, build. Ignore the pose."*
   It is the asset's `sheet`, never its `original`, and in the run it rides
   **second**, labelled *wardrobe and angles — face from IMAGE 1*. A photo
   that already shows what every cut needs gets no sheet at all: that is one
   fewer image against the run's budget.

   **When the console's Tone is one of the six era tones and the photo is plainly of today,
   offer an era-adapted portrait once — never apply it unasked (2026-09-07).**
   Hair, make-up, wardrobe and grooming restyled to the era, the face kept;
   the trade-off said in the offer — the identity is then a generated image,
   not the photograph. Chosen, it is judged on the board and becomes IMAGE 1
   as a recorded derived file; declined, nothing changes
   (`references/ASSET-SHEETS.md`, *The user's photograph*).
4. **One save, locked, no board for the photo.** `picsart_save_asset` once per
   photo-sourced asset, at the same moment the generated sheets lock:
   `portraitUrl` = the original, `turnaroundUrl` = the approved angle sheet
   when one exists, `status: "locked"`, no `stressTest`; the `film.json` row
   carries `source: "user"`, `original`, and `sheet` when there is one. The user gave
   the picture; asking them to approve it is a stop with no decision in it —
   only the sheet, if any, was a question, and it was answered on the board.
   The descriptor is **minimal and written from the pixels**: tag, role,
   behaviour, and costume only where a scene departs from what the photo
   shows — a descriptor that misreads the hair fights the photo in every cut.
   Show the line once; no round. A sheet approved later is a **new version, a
   new save** (`sheetVersion` v2, same `portraitUrl`), never an overwrite.
5. **In the run the photograph goes first, as a photograph.** the references header:
   *"IMAGE 1 — IDENTITY: a photograph of the real @cal. Match this face
   exactly — face, hair, build."* Untouched when the original is a single
   view; when it is a composite, IMAGE 1 is the identity view cut from it and
   the line says so — *"the adult-in-formalwear view, cut from the four-panel
   reference she supplied"*. When the photo's background is not one of
   the film's places, the same line says so: *"the background of IMAGE 1 is
   not a location in this film"* — words first, that is how a kitchen behind a
   portrait stays out of the desert. A **derived copy** (`assets[].derived[]`,
   a new file with its `op` recorded) exists for four hard reasons and one
   repair, never as an intake step: **a composite cut into its views** (the
   only one that is routine, above); a format the model does not accept (a
   phone's HEIC — a JPEG copy, same pixels); the run's byte ceiling (JPEG
   re-encode, then downscale while the face stays ≥ 600 px); the model's size
   floor (a photo with either side under 300 px — `picsart_enhance` a copy); and a
   background that leaked into the first run despite the words (a
   neutral-grey cutout of a copy, `picsart_remove_bg`). The original in the
   library is never the file that changes.

The same shape for a **product** (the photo is the object; an angle sheet only
when the cards turn it) and a **real location** (the user's photographs are
`SPACE` in every run there; the scene picture is generated with them attached
and the room map is made from the best wide photograph —
`references/ASSET-SHEETS.md`, *The location*).

## The conveyor — four steps, one board, then go

1. **Draft every descriptor in one turn**, from the story, the reference
   captions and the setup console — all of the block's characters, locations
   and props. This is where the text portraits and location descriptions are
   written (2026-09-10 — the folded Stages 2 and 3 used to claim them).
   Exhaustive and concrete: costume by item with materials and condition ("a
   worn dark-blue work jacket", never "a jacket"); anything invented beyond the
   script carries a `design proposal` note. **There is no separate approval
   round for the text**: the descriptor rides onto the review board as each
   tile's `prompt`, its key lines as `info`, and the user approves picture and
   words together. A note that comes back on a tile lands on one descriptor line
   and one panel. **If the user supplied an image, look at it first**
   with `picsart_view_image` (free) — a URL is a link, not pixels — and decide
   what it is: a photo of the thing itself is the asset (*When the user brings
   the asset*, above), and no sheet is drafted for it here; a reference
   informs the descriptor and stays a reference. **When the console's Tone is
   not Auto, write the cast to the tone's era** — hair, grooming, make-up,
   wardrobe, screen archetype (2026-09-07): the sheet inherits grain and colour
   through the style prefix, but a 2024 haircut inside an 80s tone is a render
   fighting itself. When the story's period disagrees with the tone's era, say
   it **once** and let the user decide — dress the cast to the tone, or set
   Tone to Auto; never restyle silently, and never touch a real person's photo
   for it (the offer in *When the user brings the asset*, step 3).
2. **Generate every ready independent asset in one approved batch.** Compile
   one sheet per generated character and one plate per prop. Read the live
   image catalog once and record the model ids in `film.json.model.pictures`.
   For a one-shot sheet, choose a model whose schema supports 4K on either
   style path; preserve the locked era and medium in its compiled prompt.
   Use the maximum supported resolution and verify that every identity panel
   clears 1024 px on its short side. Split an overcrowded grid into another
   sheet only with the additional generation priced and approved. A supplied
   photograph uses the chain in `references/ASSET-SHEETS.md` instead.
   Preflight each concrete request with `picsart_preflight`, show the combined
   quote, and dispatch only the authorized calls via `picsart_generate` with
   `async: true`. A dependent call waits for the actual checked input URL.
   Put all returned handles on `picsart_render_monitor` and reconcile each
   with `picsart_job_status`; a timeout is not permission to generate again.

   **Every asset prompt opens with the task-scoped asset style prefix and one medium
   line for the film** (`references/ASSET-SHEETS.md`, *Every image prompt
   carries the film*) — a portrait, a sheet or a plate that comes back in
   another medium, a drawing on a live-action film, is a re-dispatch inside
   the batch, never a tile. **Every sheet prompt is the handbook's T-cut prompt for the path** (grey for white;
   on the vintage path the film's stock word for *Technicolor*), and a states sheet declares its grid — one
   figure per panel, nothing crossing a gutter, no captions — because a sheet
   that cannot be cut cleanly cannot reach a run. Keep the monitor current
   while the approved batch runs, and show ready previews as they land. If a call under-delivers (a panel missing, two people on the
   sheet, the wrong medium), diagnose and correct that asset within authorized
   spend; do not resend identical failing instructions or hold ready siblings
   (`references/ASSET-EFFICIENCY.md`).

   **Show each asset as soon as it lands; check it before use.** Use the
   lightweight preview from `film` rule 4, and inspect it with
   `picsart_view_image`. Check identity across panels, age, medium, one person
   per panel, clear gutters and absence of burnt-in captions. The preview may
   be shown before the check finishes; explain a defect under it and offer
   the bounded repair. Locking, cropping and using it wait for the check and
   user approval. Use full-size originals for crops and generation inputs.

3. **Review the ready subset immediately, one tile per asset, deduped**: `picsart_asset_review`
   with the ready sheets and plates (split past 12 tiles); pending assets wait for their own review, each tile's
   `tag`, `prompt` (the descriptor), `info` (type, scenes served), and a
   `judge` line that names the question — *"the same person in every panel:
   face, build, costume. Ignore the pose."* — one question per tile, chosen
   from the handbook's checklists for a portrait, a sheet, an angle sheet and
   an era-adapted portrait, which are what you check yourself before the asset
   is locked or used (`references/ASSET-SHEETS.md`, *What the board judges*). Pass `imageModels` from a live
   catalog read so a change can be re-tried on another model. Feedback per
   `../film/references/WIDGET-FEEDBACK.md`: `approved[]` and `changes[]`,
   `approved.length + changes.length === total`, nothing decided by silence.
   - **Every `approved` sheet is locked on the spot, silently** — see step 4.
   - **Every `changes` sheet is one surgical repair**: the note folded
     into its own prompt (one descriptor line, or one panel named), nothing else
     touched; a **fresh board with only the returned tiles**, `version` bumped,
     `previousUrl` set so "go back to the first one" is a click. Approved sheets
     never reappear. Loop until `changes` is empty. Up to three named `models`
     → one version per model as sibling tiles on that next board, the only
     sanctioned case of two tiles sharing a tag.
     Before rebuilding dependents, check which content actually changed under
     `references/ASSET-EFFICIENCY.md`; a version bump alone is not a cascade.
   - **A point fix is a mask, not a re-roll.** A collar, a scar, a wrong buckle
     on an otherwise right sheet goes to an image **edit** model (the newest on
     the catalog that takes a mask, `purpose: "edit"`) as an
     inpaint of that panel; a full re-render re-rolls the 95% that was right.
4. **The approval IS the lock — and the lock is where the sheet is cut.** Per
   approved asset, in one step: probe the sheet, read the panel boxes off it,
   crop the
   views the cards name (the identity view always; the wardrobe view; one per
   tagged panel) — **cut at
   the sheet's full pixels and scaled down to the 2048 px delivery cap in the
   same export**, because the video model renders at 1080p and declares no
   input pixel limit of its own — then probe and eyeball each crop, and save.
   **No view leaves this step under 320 px on its short side** — width counts as
   much as height, a narrow panel is scaled *up* until it clears the floor, and
   a panel that needs it means the sheet was too small (say so). That is the one
   check a 252×508 wardrobe view got past on 2026-09-09, refused by the provider
   mid-run for its width. The recipe, the mandatory `content.bounds`
   and the three checks are in `references/ASSET-SHEETS.md`, *The crop* — and
   the sheet arithmetic that catches an undersized sheet before any of it is
   *Before the first cut*. An
   export costs render time, not credits (2026-09-07), so cut every view the
   cards name — but cut them **here and not before the board**, because a sheet
   the user sends back changes its panels. One state is one box and one file,
   reused by every run that names it.

   One `picsart_save_asset` per approved asset, with `status: "locked"`,
   `projectFolderUid`, tag, type, name, descriptor lines and `sheetVersion`.
   For a one-shot character, `portraitUrl` is the cropped front-face identity
   view and `turnaroundUrl` is the approved sheet; crop the profile from its
   panel. For a supplied photograph, preserve that original as `portraitUrl`,
   with the derived sheet as `turnaroundUrl`. A prop uses its plate as
   `portraitUrl`; the location uses its map when saved at `film-scenes` step 3.
   **`referenceImages` =
   the view URLs** (that is the field the library keeps them in; `film.json`'s
   `views[]` carries the richer `{ panel, position, box, url, bytes }` map),
   `scenes`. No `stressTest`:
   since 2026-09-05 the save tool locks on the approval alone — there is no
   repeatability battery anywhere in this flow, and `picsart_asset_sheet` is
   disabled on the server. Read `locked` in the result, never `saved`.
   Run independent save calls in parallel; report each ready cast subset in one line: *"These assets are locked; their scenes can begin."* — and route to `film-scenes`. Writing `film.json`
   (`assets[]`, `bible`, `gates`) is silent.

   **The film uses the URLs the save hands back, never the ones the export gave
   you (2026-09-07).** `picsart_save_asset` re-hosts `portraitUrl`,
   `turnaroundUrl`, `referenceImages[]` and the voice sample into the durable
   bucket as it saves, and the result's `manifest` carries what it settled on
   (`manifest.portrait.url`, `manifest.referenceImages`) — those are the URLs
   `film.json` records and a run's `imageUrls` carries.
   `picsart_list_assets` is the same truth later. When the message comes back
   with the expiring warning — the durable grant is not in place for this
   service yet (`../../docs/PLATFORM-ASKS.md` #6) — say it in one plain line
   and keep the sheet's prompt, grid and panel boxes, because those are what
   lets a view be re-made. A `picsart_drive` upload adds nothing the save did
   not already try: it runs the same best-effort re-host and reports the same
   `durability`, and when the re-host is refused Drive registers the source URL
   verbatim — an expiring copy of an expiring link.

   **A URL that 404s later is a re-cut, not a re-generation.** The views come
   off the archived sheet at boxes `film.json` already holds; the crop is free
   and deterministic, so the file that comes back is the same pixels. Only a
   lost *sheet* is a re-generation, from its saved prompt — and that is
   charged, so it gets a quote and a yes like any other purchase.

   **Approving the sheets locks the descriptors** (the look itself was locked on
   the console at Stage 1). The style prefix was compiled
   from the setup console at Stage 1 and is what every sheet was generated
   under; the text portraits are the descriptors on the tiles. Nothing is
   re-approved in words — `bible.stylePrefix` was recorded verbatim at the
   console's Lock (`film-development`, Stage 1 step 2) and is read from there.

   **When a descriptor is amended to match what the model will render, record
   what it replaced and why** — *"cardigan — the uniform tunic would not
   reproduce on flux-2-pro across 4 attempts"* — in the descriptor's notes.
   Say it to the user; never rewrite a locked line silently to match an output.

## What the library holds, and the gate into Stage 6

- **Registry**: `film.json.assets[]` — tag, type, version, sheet URL,
  the grid, the panel map **with each panel's position**, `views[]` with each
  view's box and URL, scenes, status — updated on every lock and every new
  version. `picsart_save_asset` is the ONLY writer of the library
  `picsart_list_assets` reads (the views go in its `referenceImages`); a sheet
  or a view uploaded as a plain `picsart_drive` file is a picture nobody can
  find. Read the library, do not remember it: `byStatus` is the truth.
- **Views×tags check**: every tag a run's cards name resolves to **one view
  file**, not to a panel of a sheet. A tag with a panel but no view is a cut
  with no pixels for it, and the fix is one crop. Same arithmetic as the check
  below, one level finer, and run at the same moment.
- **Scene×assets check**: every tag a scene names has a
  `locked` row (a place's row locks with its pictures at `film-scenes` step 3), and every tag whose card uses a **panel** (`@cal_wet`, a
  profile) has a `sheet` — a photo-sourced row without one passes the first
  test and fails the second. This is arithmetic on `film.json`, run silently
  before the first run dispatches; a hole is a sheet nobody generated, and the
  fix is to generate it — not a board, not a conversation. **A run generates only when
  every scene it spans is covered**, and that is the one gate here.
- **Read the location column in script order** for state the story changed: a
  scene after the fire is a dependent run that carries **frames from the run
  that burnt it**, never the clean pictures (`film-scenes` step 6). The
  check cannot see it; only reading can.

## After the run — repairs that used to be pre-production

- **A face that drifts in the run** is a re-roll or an edit pass
  (`film-scenes` step 5), judged on the review board — one generation, not a
  battery. If it drifts twice on the same face, tighten the descriptor's face
  lines and re-roll; if the sheet itself is the problem (face panel too small,
  two panels disagreeing), regenerate the sheet and re-cut its views. **Check
  the view before blaming the model**: a crop with a neighbour's shoulder in it,
  or a run that went out carrying a whole sheet, explains a drifting face on its
  own, and costs one re-crop rather than a generation.
- **A two-shot that blends two faces**: fewer references — each person's
  identity and wardrobe view and nothing else — a medium two-shot rather than
  a wide; try the alternate pictures model from the live catalog (`../film-scenes/SKILL.md`, step 3,
  2026-09-09). Not a composite of two solo renders: once the set was tight the
  2026-09-08 film never needed one.
- **There is no repeatability battery and no sheet board (2026-09-05).** The
  run is the check. `picsart_asset_sheet` is disabled on the server — do not
  look for it, do not describe it. A user who wants to see a face hold before
  the run gets it the cheap way: one extra still on the review board beside the
  sheet, judged by eye.
- **Every speaking character gets a voice description.** Choose it from the
  character and story without an extra user question; save it in the character's
  `voice` field before their first speaking generation. Follow the VOICE row in
  `references/ASSET-SHEETS.md`. Native audio still uses this description in every
  speaking prompt; it does not require a voice picker, sample or cloning pass.

## Costs

| Free | Charged |
|---|---|
| descriptors, the review board, the registry and the scene check, preflight, reading panel boxes off a sheet (`picsart_view_image`, `picsart_media_probe_media`), saving and locking a user's own photo, and **cutting the views** — `picsart_media_export` costs render time and a Drive file, not credits (2026-09-07), so views are never rationed; they are cut at lock only because a sheet must be approved before its boxes are final | one sheet or plate per generated character or prop (`picsart_generate`, image rates — quote the batch once; a place's pictures are charged at `film-scenes` step 3), an angle sheet from a user's photo when the cards need one, a profile and sheet derived from a supplied photograph, an era-adapted portrait when the user chose it, any change round |

## Traps

- Generating a passport, a battery and a matrix out of habit. Removed
  2026-09-04, and the sheet board itself is disabled on the server since
  2026-09-05: one sheet, one approval, the run is the check.
- Boarding a picture nobody looked at because the viewer said "too large".
  Use the preview path in `film` rule 4 and look; six hours of unseen pictures cost a film its
  heroine's hair on 2026-09-08.
- A sheet with two people on it, a face panel too small to read, or figures
  spilling across the gutters. A weak face panel is a weak film, and a sheet
  that cannot be cut into clean views cannot reach a run at all.
- **Passing a multi-panel image to the video model at all** — a sheet, or a
  composite the user supplied — and trusting a worded pointer to pick the
  panel. That is sc04a and sc04b: four faces in one picture, the right one
  named, a different wrong one rendered each time — and one frame a blend of
  two panels, a face not in the cast. Cut it. There is no wording that fixes it
  and no fallback pointer; if the crop path is down, the run waits
  (`references/ASSET-SHEETS.md`, *A panel pointer is a dispatch error*).
- Two adult states of one character on one sheet, unflagged. It is the pair
  most likely to blend; separate them on the grid, keep them as two view
  files, and say so when the sheet is designed.
- A character sheet with a scenic background. Neutral grey is not aesthetic —
  a location behind a reference leaks into every shot that reference touches.
- Generating a location here, empty, from its descriptor. The handbook's order
  is scene first, place from it — an empty place renders unreal (2026-09-08).
- Evaluative descriptor words ("beautiful", "stylish"). If a costume department
  could not buy it from the description, the model cannot hold it.
- Re-approving in words what the user already approved as a picture — a
  descriptor round, a bible round, a "gate passed" round.
- Descriptor edits that rewrite instead of touching one line.
- Re-encoding, enhancing or cutting out a user's photo on the way in — or
  generating a lookalike from a descriptor when the user gave the real face.
  The original is saved byte-for-byte and is what IMAGE 1 comes from; every
  changed copy is a new derived file with its reason recorded, and only for a
  composite cut into views, the byte ceiling, the size floor, or a leak the
  first run proved. **Cutting a composite is not an exception to this** — the
  original is untouched and the views are cut from a copy.
- Putting a user's own photo on the review board. They gave it; only a sheet
  generated from it is a question.
- Redrawing a state panel from scratch on a new sheet because i2i is down. A
  from-scratch `@seneb_swollen` is a different man than `@seneb`; park it and
  retry when the endpoint recovers.
- Restyling a user's photograph to the tone's era without being asked, or
  dressing a generated cast in today's hair and wardrobe under a vintage tone
  without saying so. The era offer is made once and answered; the mismatch is
  said once and decided (2026-09-07).
- An asset prompt without the film's medium and compatible visual styling. Use
  `bible.assetStylePrefix` for neutral references, not scene staging. The picture otherwise
  belongs to no film, and a cartoon on a live-action drama is what comes back
  — and it is a re-dispatch, never something the user is asked to regenerate
  (2026-09-07).
- A back panel on every sheet by habit — face pixels spent on a shoulder; the
  four-panel sheet only when a card needs the back. Swapping a step's model for
  resolution's sake: the handbook's pairing stands while the user tests GPT
  Image 2's output as it comes (2026-09-07).

Referenced files: 2

film-development21 KB

View saved version →

---
name: film-development
description: "Develop a film from an idea or a script to the point where its pictures can be made — in a fixed order: the look console first (genre, tone, camera — locked before a word of story), the story in the viewer's words (ten lines, then every scene as what we see and hear) for the yes, the video model, the video plan (one line per video, one sentence per join), then the shot list board for camera. Use for Stage 1 of the film pipeline (Stages 2 and 3 are folded into it since 2026-09-10): the user has a story idea or a script and nothing is locked yet. Routed from the film skill; for generating shots see film-scenes."
---

# ChatGPT and Codex integration

Apply these host conventions throughout this skill and its references.

- Discover the connected Picsart tools and use their actual names and schemas.
  The `picsart_*` names in the workflow identify tools, not a fixed host namespace.
  If the connection is missing, explain that Picsart must be connected; continue
  script and shot planning when possible. Never invent tool results or renders.
- Read board decisions from the current user message and attached widget context.
  Use `read_widget_context` only if the host actually exposes that tool. Do not
  assume ChatGPT has Claude's context reader. Treat a board's initial tool result
  and unsent widget state as previews, never as user approval. If a submitted
  decision has no accessible payload, explain briefly and use the text fallback
  to collect the missing decision; do not guess or repeat charged operations.
- Use the host's available question interface or one concise conversational
  question for preset choices.
- UI rendering and delivery of feedback are the remote MCP server's responsibility.
  If a board is unavailable, present its relevant options in text and record the
  user's selection using the same documented feedback fields. Do not claim that
  a plugin manifest makes Claude-specific widgets compatible with ChatGPT.
- Save `film.json` and artifacts in the user's writable project workspace, not
  the installed plugin directory. If persistent file tools are unavailable,
  keep a structured project checkpoint in the conversation and provide it for
  download when the host supports files. Never claim a file was saved without
  confirmation from a file tool.
- Honor the user's current instructions and already authorized scope. Preserve
  quoted spend limits and production locks; do not infer authorization from a
  preview, a timeout, or a connection being installed. A timed-out charged call
  needs a status check before considering a retry.
- This package holds six film skills (`film`, `film-development`, `film-assets`, `film-scenes`, `film-edit`, `film-finishing`) and three motion skills (`motion`, `motion-import`, `motion-design`). Route between them directly.
- `motion-import` reads a design's layers through the Figma app's tools. If Figma is not connected in this host, use its flat-frame fallback and say so once.


# Development — look, story, model, video plan, shot list

Everything in this skill is **free** (except optional candidate stills, which are
image-cheap). It is also where the film is actually made: a director who walks
out of Stage 1 with a tight bible spends half as much in Stage 6. Do not rush
it because nothing here renders.

## Stage 1 — six steps, in this order, always (2026-09-09)

The order is the rule. Say where you are in it when a step opens — *"Stage 1 ·
step 3 of 6 — the story, for your yes"* — and when the user asks about the
order, quote the step, never answer from memory.

1. **The idea.** Ask for a premise only if none was supplied. Pre-set duration
   and frame on the look console from the user's words or stated destination;
   otherwise use 90 seconds and this plugin's existing 16:9 default. The user
   changes them on the console. Propose the ending in the story rather than
   asking another intake question.

2. **The look console — before a word of story is written.** Open
   `picsart_film_setup` with `suggested.selections`: genre, tone, `palette: "auto"`,
   ratio and duration from step 1, camera and lens family. Choose a colour scheme
   and hue only when the idea names a colour; otherwise leave `scheme`, `hue`
   and `hueDegrees` out. When pre-setting a hue, include its wheel angle from
   `../film/references/PRESETS-LOOKS.md`. Give one line of why per inference.
   Focal length, aperture, light and an individual shot's palette belong on
   the shot list. The setup locks the style prefix, lens family and colour
   scheme before the story or shot list is written. A tone seeds only camera
   and lens; colour changes preserve the tone.
   The user's submitted Lock is the look's lock. Store the compiled prefix
   verbatim in `bible.stylePrefix` and the selections in `bible.setup`, keeping
   `hue` and `hueDegrees` exactly as returned. The feedback and per-tool compile
   mappings are in `../film/references/WIDGET-FEEDBACK.md`.

3. **The story — one plain document, shown in chat, for the yes.** Written for
   the locked genre, in the viewer's words (`references/DRAMATURGY.md`, *The
   story*): ten lines at the top — who, where, what each wants, what stands
   in the way, what happens, which beat the film is built toward, how it ends,
   the one thing the viewer is left with, what the user's brief fixed (quoted,
   never assumed), the genre — and then **every scene as what we see and hear,
   in order**: one plain paragraph per scene, the lines in quotes, ending with
   its length in seconds. No camera words, no film vocabulary, no production
   notes: a reader who knows nothing about film reads it as the film. Inside a
   stated identity budget ("this fits two faces and one place — here it is
   inside that"), inferred from the idea, never asked. The user says yes, or
   says what to change; a change is a rewritten paragraph in the next message,
   and the yes comes on the rewritten text. Recorded: the ten lines as
   `film.json.story`, the scenes as `docs/story.md`. *(This replaces the
   scenario-plus-script pair of the morning of 2026-09-09, and the script file
   nobody saw on 2026-09-08: that script went to `docs/script.md` at 11:10
   unshown, the user first read the story as a plain paragraph at 18:01, and
   had real notes on it within a minute.)*
4. **The video model.** Read `picsart_model_catalog` (`mode: "video"`,
   `purpose: "generate"`), filter by the console's ratio and by native
   multi-shot at the finish resolution, and put two or three survivors on
   `picsart_model_choice` with exactly one badged `recommended` (the `film`
   model policy carries the filter). Record `film.json.model.id`,
   `shoot.ceilingSec` and `shoot.editModel`. The pick belongs here because the
   video plan cannot be written without it: the model's ceiling is what fills
   each video, its `imageUrls` max is the reference budget, and its video-edit
   sibling is the repair path.
5. **The video plan — reviewed with the shot list.** Against the selected
   model's live ceiling, assign each shot to a run and write one sentence
   explaining each run boundary, including boundaries inside a scene. Save
   the run ids, scenes, `cutSec`, `genSec` and `join` in `film.json.runs[]`.
   Present the plan once on `picsart_shotlist_board`: each shot carries `run`
   and the opening shot carries `join`; use the board's `note` for the concise
   run-length summary. This tool has no top-level `runs` input. The board's
   submission approves the plan and shot list together. Text fallback shows
   the same grouping and asks one lock/change question.

6. **The shot list — one board, all scenes, camera only.** Below.

**Combine story approval and model selection where the host allows it.**
Show the story and `picsart_model_choice` in the same response, with two or
three live-catalog candidates. Say explicitly that choosing one accepts this
story and selects the model; a story note reopens the paragraph first. If the
host does not deliver a combined decision, collect the missing approval
explicitly. This reduces turns without inferring consent from a preview.

**One rule with the order: if the genre changes at any point** — the user
re-opens the console, or says "make it an action film" — **the story is
rewritten first**, before any picture or shot list moves. A drama script under
an action lock is what a whole day was lost to on 2026-09-08.

**A story note is answered with a paragraph, never a board (2026-09-09).** When
the user's note is about what happens — who does what, who has what, where
they are, what is said — rewrite that scene's paragraph in the story, show it,
get the yes, then rebuild the cards it touches and re-open the board once.
This holds at every stage: on the board, after a picture, after a video. On
2026-09-08 "Nen goes and grabs the water" was answered with nine shot-list
boards over three hours, one of them for the wrong scene; the same note is a
one-minute rewrite of one paragraph.

**The identity budget never buys silence.** Do not offer a mute character, a
wordless treatment, or a voice-over-instead-of-dialogue version as a saving or a
safety measure: the film's video model does native lip-synced dialogue in-shot,
so people talk. Silence is a directing choice the story earns, and if the user
asks for it, give it to them gladly.

**Action is not a cost flag, and neither is anything else in the story.** Until
2026-09-09 the breakdown flagged action, crowds, water and fire as "complex"
and proposed a cheap rewrite beside each; it is how a film about two women
fighting for water was written as "grip and weight, never blows" and stayed
that way for seven hours. The video model was chosen for the film's genre; the
story decides what is in it. The one thing still written around is in-frame
text (signs, letters) — models write text poorly, so it is added in the edit,
never generated in-shot.

**When the user brings their own script**, keep it: it is the story document,
and step 3 is already done. Read it once for the two things the medium cannot
do — in-frame text, a character's age as a number — and say them in one line
each as suggestions. Nothing else is reviewed; the video model and the video
plan follow.

## The shot list

**The board approves camera (2026-09-09).** It opens once, after the story and
the selected model are approved; this board also approves the video plan. What the user changes on it is camera — size,
movement, lens, light, colour — and seconds, and whatever they change is
written into that shot's block, never lost
(`../film-scenes/references/PROMPT-BLOCKS.md`, Part 2, *The card's own picks*,
2026-09-10); a card's action and line are copied from
the story's paragraph and are never rewritten on the card. A note about what
happens is a story note (above): the paragraph first, then the board rebuilt
from it. Never open a board to show a story — on 2026-09-08 the board was
where the user read the story, and every story change cost a board.

Turn the story into two lists, per `references/BREAKDOWN.md` (load it when
actually breaking down):

1. **The shot cards** — every shot as a card: scene and shot id, location, time
   of day, who is in frame by tag, props, the action in one to three
   sentences, the line verbatim or a dash, seconds; who stands where relative
   to the camera; what the character feels; the camera — size, movement, lens,
   angle. These are not notes for a DP — they are future prompt lines.
2. **The asset list** — people, places, props: every character, every place
   the film plays in, the props the story is about, each with a future `@tag`.
   A durable change the story does to a place or a person — the room after the
   fire, the shirt once it is bloodied — is a row too, because the model
   generates the clean version otherwise; anything that resets between scenes
   is not. Nothing is tiered and nothing is flagged: every character gets the
   handbook's three pictures, every prop a plate, every place its pictures
   from the scene (`film-assets`, `film-scenes` step 3).

**Shot length is a habit, not a gate.** Dramatic shots run 3–8 seconds, action
shorter, a reaction shorter still — the handbook's *"Use the duration based on
what the shot needs."* A shot over 8 seconds carries its reason in one line on
the card and nothing else. Never lengthen a card to fill a video; the video is
filled with short shots (`film-scenes`, *The run pass*).

Present the shotlist through `picsart_shotlist_board` — **one call carrying
ALL scenes** (`scenes: [{id, slug, shots}, …]`), never one board per scene:
the board's single approval covers exactly the scenes passed (its feedback
names them in `scope`), which makes "one confirmation covers a set" mechanical
instead of a guess. Pass `model`. Seed every pill from what is already decided
— the scene's light, the focal length the shot size calls for, and the camera
move. Seed `palette: "auto"` unless the shot needs its own colour; any selected
palette must fit the film's scheme and hue. Surface compiler conflict notes
before dispatch. Pass every shot's lens as `fov` and its aperture to the prompt
compiler, whether seeded or changed, and include each non-auto palette's Colour
line. The user touches only what they disagree with. Without the server board, a
markdown table per scene with one "anything to change in any scene?" at the
end.

**Call the board and say nothing.** It already carries its own instruction —
each scene's header reads "N shots · Xs — type straight into any shot" — and the
preset chips are visible pickers that show film language rather than asking for
it. If a pass genuinely needs framing the board does not already give, pass it
as the board's `note`, not as a message. Feedback per
`../film/references/WIDGET-FEEDBACK.md` — an `approved` verdict may carry
staged edits (apply them first), cell edits apply to the cards, `removed`
shots leave the scene. The board returns the fields it owns — action, line,
tags, values, order, cuts — and those replace yours wholesale; what it never
carried (`design`, `refs`, `run`, `trim`) stays on the shot by id. A changed
action that tells a different story than the story's paragraph is a story note:
the paragraph is rewritten and shown first, never straight into a prompt, and
never answered with another board.

Sum running time by the cards' seconds. A script page is roughly a minute, but
the seconds column is the number that counts.

## Stages 2 and 3 — folded into Stage 1 (2026-09-10)

**References are optional inspiration, not a stage.** The look is locked on the
console at Stage 1 step 2, and nothing gathered afterwards reopens it. When
the user has images — their own uploads (`picsart_media_upload` opens the
upload panel), film stills they name — **look at each one before captioning
it**: export a 1280 px JPEG with `picsart_media_export` and view it
(`picsart_view_image`, free); a URL is a link, not pixels; never web-fetch an
image URL and never caption a reference you have not seen. Every image gets a
caption, *what exactly we take from it* ("light from the window", "jacket
fit"), and the captions feed the descriptors at `film-assets` step 1.
Anti-references — "like this: never" — become bans written into prompts in
positive form. Counts and categories, for the user who wants a board, are in
`references/VISUAL-BIBLE.md`. **This spends nothing**: a generated moodboard
is never an automatic charge, and the first scene picture doubles as the tone
moodboard.

**A photo of the thing itself is not a reference — it is the asset
(2026-09-04).** When the upload is the real person, the real product, the real
place the film is about, save it exactly as sent (a Drive copy, probe-matched
byte for byte) the moment it arrives; it registers locked at Stage 4 without
a board, and goes to the video model untouched as IMAGE 1 — `film-assets`,
*When the user brings the asset*. Decide by looking: film stills, artwork and
mood photos are references and never reach `imageUrls`; a person, product or
place is identity. Ask only when an image genuinely reads both ways, one short
question.

**The visual bible is the console's output, and the console's Lock is its
lock.** `bible.stylePrefix` is compiled by `picsart_film_setup` at step 2 and
stored verbatim; there is no later lock and no prose put in front of the user
to approve — they see the prefix's effect on the sheets. The text portraits
(appearance, age, build, costume by item, movement, posture) and the location
descriptions (architecture, textures, light, condition, atmosphere, palette)
are written at `film-assets` step 1, when the sheets are about to be
generated, and approved with the sheets. Design so the look **rhymes with the
character**: every visual choice — scar, fabric, colour, the state of the shoes
— restates who this person is and what the story does to them; a detail that
says nothing is a detail the model will vary freely, and a detail with two
readings ("dark, bleached at the crown") is resolved by the model inventing a
third. If the genre moves later, the story is rewritten first (Stage 1's rule)
and the console is re-run. The story's period is **not** one of the console's
controls — it lives in the location descriptors and the scene context, never
in a look preset. A film with two visual worlds runs the setup once per world.

*(Until 2026-09-10 Stage 2 gathered boards and Stage 3 "locked" a bible the
console had already locked, both after the shot list had been approved with
its camera; the docs gave four answers to when the look locked — step 2, a
Stage 3 gate, a Stage 3 gate item, the Stage 4 sheet approval. The stage
numbers stay so nothing downstream renumbers; the work moved up into step 2.)*

**The voice policy is `native`, and there is nothing to decide (2026-08-27).**
Each shot's generated voice is the performance. Record
`film.json.voicePolicy: "native"`. Every speaking character also gets a canonical
voice description on its asset sheet, chosen without an extra user question and
copied verbatim into every speaking prompt; see
`../film-assets/references/ASSET-SHEETS.md`. Native does not mean unspecified.
Description consistency reduces drift but does not guarantee an identical voice.
Review recurring speakers across video edges; a finishing bed can smooth room
tone and level changes, not a changed speaker identity. A separate voice pass is
optional, not a prerequisite for writing or generating dialogue.

**Optional gate-out deliverable: the pitch package.** Everything a pitch needs
already exists at this lock — assemble `docs/pitch.md` (logline, the bible's
tone and style prefix, cast portraits, key boards, planned runtime and budget
range) referencing the Drive-saved copies of the boards, never bare CDN links
(they expire). One free assembly step; offer it, don't push it.

On a short film **one batch is the whole asset list**: stills are cheap, so
Stage 4 generates every picture in a single pass and the user approves all of
them on one board (see `film-assets`). Splitting into production blocks is for
genuinely large lists (≳15 assets) or when the user asks — never the default.

## Gate out (to `film-assets`)

The look locked on the console · the story and the video plan with their yes ·
the video model recorded · the shot list approved. Update `film.json`
(`bible`, `gates`) silently and route straight on to `film-assets`, in the
same turn.

## Costs

| Free | Charged |
|---|---|
| interview, console, story, video plan, shotlist board, captions, all text | candidate stills via `picsart_generate` (image rates — a fraction of video; still preflight and mention it) |

## Traps

- **Front-loading the story before the console.** A story written as a drama,
  then locked as action, is two films; the order above exists because this
  happened (2026-09-08).
- **A story the user never read.** A one-word yes on a table of beats is not a
  yes on the film; the story is shown as paragraphs and the yes comes on those.
- **Answering an order question from memory.** Quote the numbered step.
- **Answering a story note with a board.** The paragraph is rewritten first;
  the board is rebuilt from it once.
- **Writing the story in film words** — "turn", "payoff", "beat", "OTS", "grip
  and weight, never blows". The story is the one artifact that exists to be
  judged as a film by someone who is not making it; camera belongs on the
  card and production notes in `film.json`, so a story written in production
  terms cannot be judged for what it is. (On 2026-09-08 the story became
  readable only at hour seven, as a plain paragraph.)
- **Writing the fight soft to protect the render.** Nobody measured that; the
  genre decides, and the model was chosen for it.
- Skipping the durable-change rows ("we'll handle the wrecked room later") —
  the missing picture surfaces mid-production.
- References without captions, or all beauty and no light/optics — light and
  optics are what the model follows best.
- Filing the user's photo of the actual person as one more mood reference and
  generating a lookalike from a descriptor. The photo is the asset, saved
  untouched, first in every run.
- Writing the visual system as taste ("moody, cinematic") instead of prompt-ready
  words. If it cannot be pasted into a prompt, it is not locked yet.

Referenced files: 4

film-edit28.7 KB

View saved version →

---
name: film-edit
description: "Edit the film from selected takes to the cut the user calls final — scene assembly, evidence-based join repair, reshoot orders, review and the user's own final cut. Use for stages 7–8 once scenes have selects, or when the user wants a better join between existing clips. Routed from the film skill; a one-off montage uses the same compositor without the pipeline around it."
---

# ChatGPT and Codex integration

Apply these host conventions throughout this skill and its references.

- Discover the connected Picsart tools and use their actual names and schemas.
  The `picsart_*` names in the workflow identify tools, not a fixed host namespace.
  If the connection is missing, explain that Picsart must be connected; continue
  script and shot planning when possible. Never invent tool results or renders.
- Read board decisions from the current user message and attached widget context.
  Use `read_widget_context` only if the host actually exposes that tool. Do not
  assume ChatGPT has Claude's context reader. Treat a board's initial tool result
  and unsent widget state as previews, never as user approval. If a submitted
  decision has no accessible payload, explain briefly and use the text fallback
  to collect the missing decision; do not guess or repeat charged operations.
- Use the host's available question interface or one concise conversational
  question for preset choices.
- UI rendering and delivery of feedback are the remote MCP server's responsibility.
  If a board is unavailable, present its relevant options in text and record the
  user's selection using the same documented feedback fields. Do not claim that
  a plugin manifest makes Claude-specific widgets compatible with ChatGPT.
- Save `film.json` and artifacts in the user's writable project workspace, not
  the installed plugin directory. If persistent file tools are unavailable,
  keep a structured project checkpoint in the conversation and provide it for
  download when the host supports files. Never claim a file was saved without
  confirmation from a file tool.
- Honor the user's current instructions and already authorized scope. Preserve
  quoted spend limits and production locks; do not infer authorization from a
  preview, a timeout, or a connection being installed. A timed-out charged call
  needs a status check before considering a retry.
- This package holds six film skills (`film`, `film-development`, `film-assets`, `film-scenes`, `film-edit`, `film-finishing`) and three motion skills (`motion`, `motion-import`, `motion-design`). Route between them directly.
- `motion-import` reads a design's layers through the Figma app's tools. If Figma is not connected in this host, use its flat-frame fallback and say so once.


# Edit — takes → one review → the final cut

The editor's material is **selects only** — never the raw generation pile. And AI
editing has one structural difference from set footage: a reshoot costs minutes,
so **the edit runs in parallel with generation and actively orders shots**: "need
a cutaway to the hands", "need a wider size between 12B and 12C". Route those
orders back to `film-scenes` as new shot cards after checking the existing
coverage. For a bad join, first read **Join repair** below: a repair is not
automatically a model call. A new connecting insert can preserve both accepted
clips; it does not require re-rolling their run. Say what will change, what stays
untouched and the cost before any charged order (`film-scenes` step 5).

## Join repair — diagnose the pair before choosing the operation

When the user wants two existing clips to connect better, read
[JOIN-REPAIR.md](references/JOIN-REPAIR.md). Inspect both sides with sound and
actual boundary frames, explain the observed failure, and recommend the smallest
repair that addresses it. Missing story information needs a motivated beat, not
a cosmetic transition. Keep the accepted cut unchanged while comparing candidates.

## The automatic rough cut is off (2026-09-04)

**A run arrives already cut.** The model made every internal cut, and a video edge
is joined by the handbook's angle change (`film-scenes/references/SEAMS.md`), so the pass this
skill used to run before anyone watched — a trim per shot derived from the
line's word timings or the beat clock, then a seam pass moving the points at
every boundary — has nothing left to derive. It is switched off. What happens
instead when a run lands (`film-scenes` step 6): the take is split at its
delivered cuts (`cutTimesActual`, compositor bookkeeping), and the scene editor
opens on the run **as delivered** — every shot a clip with the model's cut as its
bounds, no trims applied, nothing shaved. The user trims where they want to, and
only then does a shot get a `trim`, always with `trimSource: "user"`.

Three things this changes, said once:

- **No "I made a first rough cut" line, no alignment wait.** The board opens the
  moment the split is written. There is no automatic window to explain or to
  reverse.
- **The run's end handles play until the user trims them.** With `handles` on,
  the run's two ends carry unscreened seconds by design; nothing trims them now,
  so they are visible on the board and a drag away from gone. `film-scenes`
  keeps the per-film switch; this is the consequence to know when setting it.
- **`trimSource` has one live value, `user`.** `aligned` and `planned` are parked
  with the pass that produced them; read one on an old `film.json` as "an
  automatic window from before 2026-09-04" and leave it alone like a `user` one.

**Transcription still earns its keep — for "the line reads", not for trims.**
`video-transcribe-audio` (classic Picsart API plugin, when installed) is diffed
against the scripted line to catch lip-sync saying the wrong words and to verify
the one-second silence tail; without the plugin, judge by ear and say so. It no
longer places an in-point.

**User trims stay reversible, and that is a constraint on how the editor is
opened.** Author each shot as a track clip on the run's url with
`content.trim` in run-file seconds bounded by the `cutTimesActual` either
side of it — never a trimmed export — and open the editor on that scene. The
editor trims windows over the source, so widening a trim back is one handle
drag: free, instant, no round trip. Export a
trimmed clip and show that instead, and the trimmed second stops existing. Past
a delivered cut is the next shot's picture, not trim room; past `genSec` there
is nothing, and that is a retake, not a trim.

Their edits come back as `scene_editor_feedback.document` — the scene as they
left it, each piece a clip with its `content.trim` over its source, in play
order — and land straight on each shot's `trim` with `trimSource: "user"`. A `user` trim is never touched by anything automatic; a
re-rolled run re-opens untrimmed, because its cuts are new.

*Parked, for when it returns:* the derivation order was speech alignment (first
word − 0.2–0.4s breath, last word + `tailHold`, clamped before invented sound)
→ the plan (the first beat in motion, last beat + `tailHold`) → a seam
pass per cut rule. It was designed for takes born in separate generations;
a run makes it redundant, not wrong.

## What generated footage cuts like

- **It runs draggy.** Entries into action are slow. Cut more aggressively than
  feels necessary.
- **Held AI motion reads uncanny.** Viewers notice something is synthetic when
  a generated shot is HELD: dramatic beats sustain 4–7s at most, action cuts
  live at 1–2s, and mathematically even cut lengths read as machine editing —
  vary them on the emotional beat. A solo take was generated long (10–15s) as
  coverage and **the cut uses 3–8 seconds of it**; a shot inside a run was
  generated at its `sec`, so the 3–8 seconds were authored and the cut uses
  roughly all of it. On a solo take, trim into it: the best in-point is rarely
  the first stable frame.
- **The edges drift.** The first and last half-second of a *generation* are the
  least stable. Plan to trim both; that is what the shot's `trim` field is for.
  Inside a run the edges are the run's, not each shot's — the model's own cuts
  are clean and are not shaved.
- **Cut on motion, match motion.** Prefer cutting while something moves, and
  match motion direction across the seam — two static compositions butted
  together is the "AI slideshow"; a movement handed across the cut is cinema.
- **Every video edge is a suspect; an internal cut is the model's.** Shots
  inside a video were born in one generation — check its cuts once, on the real
  frames, and expect them to hold. A video edge was born in two and joined the
  handbook's way (`film-scenes/references/SEAMS.md`), so the QC there asks
  three things: the angle and size clearly changed, the movement carries
  across, and the level step is hidden by the bed. Then the classical list —
  eye-lines match, the axis is not jumped, costume/props/hair continuous, tempo
  does not lurch. Run it once at assembly.

## The cut — assemble, then one review (2026-09-09)

1. **Assembly** — author one scene with one track layer. Its `clips` are
   the shots in playback order, each with an id, source reference and its
   approved `trim`. Preserve the model's delivered cuts and the user's edits.
   The procedure and per-seam transitions are in `references/ASSEMBLY.md`.
2. **One review with the user.** The assembled film in `picsart_scene_editor`,
   watched once through. What they point at is fixed: a glitch — hands, teeth,
   a melted face — by the model's edit sibling on that clip; a bad join through
   **Join repair** above, not an automatic re-roll; a coverage hole by a new shot
   card back to `film-scenes` when the chosen repair needs new footage.
   Then the cut is final when they say so, recorded in `film.json`. This one
   review replaced the four passes (assembly, rough cut, fine cut, written
   picture lock), the join checklist, the defect list and the full-size watch.
   After it: no picture changes except a fix the user asks for — colour and
   sound work on this cut, and a silent change strands them on a stale one.

## Handing the fine cut to the user — `picsart_final_cut`

**Check `tools/list` first; this tool is not on every server.** As of
2026-09-19 `picsart_final_cut` is not served by the stage deployment the plugin
connects to — it exists, but on a branch that has not shipped. If it is absent,
**do not call it and do not announce a console that will not open**: the
round-trip editing above IS the fine cut, and the cut is final when the user
says so, recorded in `film.json`, exactly as the previous section describes.
Say once that hands-on trimming is not available on this server and keep taking
notes. Everything below applies only when the tool is actually listed.

Everything above is you editing on the user's behalf, one round-trip per note.
That is right for structure — order, coverage, which take — and wrong for the last
half-second of a shot, which is faster to *feel* than to describe. So at fine cut,
open the **Final Cut console**: a real editor in the chat, with trim, split, crop,
adjust, effects, resize, speed, volume and freeze.

**Pass the shots, not a rendered cut.**

```
picsart_final_cut {
  clips: [ { id: "12A", url: "<shot url>", label: "12A Cal turns",
             duration: 8.4, width: 1920, height: 1080,
             trimIn: 0.4, trimOut: 3.1 }, … ],
  title: "<film> — final cut", notes: "<what to look at>" }
```

- **`clips` saves an export, every time.** Each shot becomes its own layer laid
  end to end, so the console can open on the shots themselves and nothing has to
  be rendered first. Passing a single `videoUrl` instead means a
  `picsart_media_export` — and a wait, and another file in their Drive — before
  *every* edit session, and it arrives as one flat strip with the shot
  boundaries gone. Reserve `videoUrl` for a cut that genuinely is one take, or
  for reopening a flattened master.
- **Warn them the timeline does not show the shots yet.** One line is enough:
  *"the strip reads as one clip — the cut is still three shots, and Split shows
  where the joins are."* The console opens on Split for exactly this reason. A
  proper multi-clip timeline exists upstream but is parked on two editor bugs
  (2026-08-28) — `docs/widget-specs/final-cut.md` has the detail. Until it lands,
  a user who does not know this will read the flat strip as "it merged my shots".
- **Pass `duration`, `width` and `height` per clip, and pass them truthfully.**
  `picsart_media_probe_media` gives you all three and is free, and you already hold
  them from the assembly. The console then skips its own probing, which matters
  more than the round-trip it saves: a browser defers media loading in a background
  tab, so a probe there never settles and the editor sits on "Loading…". Truthful
  because these numbers become the clamp on every trim as soon as the newer editor
  lands — an overstated `duration` promises footage that does not exist.
- **It is free, and its render is free.** The preview and the export both run the
  compositing engine in the user's browser, not `picsart_media_export`. The user can
  iterate as long as they like and it costs nothing. When they save, the mp4 you get
  back is finished — same engine that drew what they were watching, so there is
  nothing to verify and **nothing to re-export server-side.**
- **Keep the replay document.** Saving returns `videoUrl` *and* `replayUrl`. Drive
  both immediately (the URLs expire), and pass `replayUrl` back on the next call so
  they resume inside their own edit instead of restarting from a flattened file. An
  mp4 alone throws the edit history away.
- **Open it once the cut is DECIDED, not before.** The shot list you pass is the
  cut's order and its keep-windows, so there has to be a cut to describe. Work that
  changes what the cut is *made of* — swapping a take, closing a coverage hole,
  regenerating a shot — stays a `picsart_media_*` operation on the scene, where it
  is free and reversible. What the console is for is the frame-level fitting of
  material you have already chosen — reordering included, since without a
  draggable timeline there is no way to change shot order in the console.
- **The scene editor judges AND cuts; Final Cut finishes.** The editor edits in
  place — trim, split, delete, duplicate, adjust, speed, effects, volume, resize
  — so "trim it a bit" no longer needs the console. What still separates them:
  the editor hands back the edited scene document and renders nothing, which is
  what keeps the next round reviewable; Final Cut renders one flattened master
  in the browser. Send a cut to the editor for notes, regeneration direction,
  and any recut that must stay shot-shaped. Open Final Cut at picture polish,
  when the cut is settled and the next artefact is the master itself.
- Full contract in `../film/references/WIDGET-FEEDBACK.md`; the widget's design
  contract in `../../docs/widget-specs/final-cut.md`.

Gate line for the final cut: **if the user edited in Final Cut, the locked cut is the
mp4 they saved** — not your assembly. Record its `stateHash` and both URLs in
`film.json` so finishing grades the picture the user actually approved.

## Assembling a scene — mechanics

The scene tools do the work: probe → author → validate → review →
export, condensed in `references/ASSEMBLY.md`, which is self-contained and, since
the `picsart-video` plugin was retired on 2026-09-10, the only version.

**Read the live editing recipe before changing the scene.** Use
`picsart_media_get_recipe({ name: "video-editing" })` (the current server
also resolves the repo's `mp-video-editing` alias), and load its relevant
schema slices. Apply trims, crops, transitions, transforms and sound changes
according to that recipe, then validate and reopen `picsart_scene_editor`.

**Every edit is a `picsart_media_*` operation on the scene — never a
regeneration, and never a file the user is asked to handle.** Trim, split,
re-order, drop a stretch, a transition, a speed or motion change, a crop, a
sound edit: all of it exists as a compositor operation, all of it is
bookkeeping over the source clips, and none of it spends a credit. The
failure this rule exists to stop is spending video rates on something the
timeline already does — re-rendering a shot to make it shorter, or to start
later, or to lose its last second, when a trim does that for nothing. If an
edit seems to have no compositor operation, say so and ask; do not reach for
`picsart_generate`, and do not tell the user to export it and fix it
themselves.

The habits that matter here:

- Bootstrap, validate and **`picsart_media_export` are all free** — iterate
  order, trims and transitions without paying, and render when you need a real
  file rather than hoarding the call. What an export still costs is TIME (a real
  GPU render) and a new file in Drive, so it is worth doing on a settled cut
  rather than on every keystroke — but it is not a budget decision.
- Before any render, put the cut in **`picsart_scene_editor` — the scene
  editor**: the authored, validated track scene, every shot a clip in
  playback order, passed as `scene`. The editor plays it in the browser, so the
  user watches the real cut with nothing rendered. **The editing there is free
  and instant** — it moves windows over the source clips, so trimming a shot's
  edges, splitting it at the playhead and deleting it all happen live and cost
  nothing. Only two things come back: the edited scene, and comments. Never
  paste scene JSON at the user. The editor takes no per-shot prompt or cast, so
  keep each clip's shot, prompt and characters in `film.json` — that is what
  turns a comment into a retake.
- **One editor, and it always carries the whole cut (2026-09-07).** The scene is
  every shot in playback order. Never an editor for a single clip, never one per
  piece of a cut the user just made, and never two open in one turn. The
  reported failure: the user split a shot on the old review board and got back
  one video view per resulting piece. Their own edit needs no second look to
  confirm it — record the cut, say in one line what changed, and open the editor
  again only for material they have not seen, with the full recut in it. If two
  are ever live at once anyway, queue them as `film-scenes` says: one editor's
  feedback is one item, and nothing advances until the queue is empty.
  **Focused join comparisons are the exception:** follow
  `references/JOIN-REPAIR.md` to review A → B against A → candidate → B without
  changing the accepted cut. An edited or extended clip is never a lone clip in
  the editor either: it goes into the cut, and the whole cut goes back up with
  one line naming the seconds that changed.
- Within a scene the default seam is a **hard cut** (`transition: ""`) — scenes
  are constructed from coverage, and continuity is the cut's job. Transitions
  belong *between* scenes if anywhere; `crossfade 0.5` is the defensible default,
  `references/ASSEMBLY.md`'s transition table for anything else.
- Draft exports at draft canvas (854×480). The final-resolution export happens
  once, in `film-finishing`, after the final-res re-roll.
- Ordinary review rounds run in that same editor, on the NEW take. Focused join
  variants use `references/JOIN-REPAIR.md`; otherwise, if the user prefers the
  previous attempt they say so and you put its url back in the cut yourself —
  free. Keep every take's url in your own plan
  bookkeeping so you can.
- **Read its feedback as two different things.** `document` (when `edited` is
  true) is an EDIT THE USER ALREADY MADE — the editor performs trims, splits and
  deletions in place, so it is not a request and there is nothing to interpret.
  `comments` (pins, each with its `layerId` and `time`) and `timelineComments`
  (stretches, `start`/`end`) are the part that carries a requested change.
  Diagnose each note's meaning before choosing compositor work or generation; a
  note about a seam is not itself permission to regenerate. Combine compatible
  notes on a shot while preserving everything the user asked to keep.
- **Recording the cut is bookkeeping, not a render.** Read `document`'s clips
  in playback order — the clips of the montage's `track` layer, or top-level
  media layers in a scene built by hand — each over its source (`assetId` into `assets[]`, or
  an inline `asset`) with `content.trim` in that file's seconds — and write each
  window to its shot's `trim` with `trimSource: "user"`; a split is two clips on
  one source, a deleted shot is a clip that is gone. Record `stateHash` beside
  the cut, and carry `document` forward as the film's scene. Validate it
  (`picsart_media_validate_scene`) before anything else is built on it.
- **The old takes never move.** Nothing already in `Shots/` is moved,
  overwritten or deleted — same rule as `Assets/`, and it is what lets the user
  come back and edit again from the original material. A render of the cut
  (`picsart_media_export`, when a file is wanted) goes in a new subfolder under
  `Shots/` (`picsart_drive action=create_folder`, `folderUid` =
  `film.json.drive.shotsUid`).
- **The next round reopens on `document`, not on a render** — and only when
  there IS a next round: new material, or a note that needs watching. The
  user's own cut is not a reason to reopen (above). Apply your changes to
  `document` (`picsart_media_patch_scene`, or a re-bootstrap from the recorded
  windows when a take was swapped), validate, and open the editor on the result.
  Its clips still reach into the whole source, so every handle can reach back
  into the material this round trimmed off. Reopening on rendered clips would
  weld each trim shut, and a shot cut a second too short could never be opened
  back up — which is precisely what the trim handles exist to allow.
- **Its direction speaks the shotlist board's language.** A camera move or a
  colour named in a comment is that board's own preset name, so
  `Slow push-in` in a note means exactly what that name meant when the shot was
  planned — any of the 50 in `PRESETS-CAMERA.md`. It all arrives as the user's
  words in a comment's `instruction` or a stretch's `text` — fold it into the
  shot's prompt rather than parsing it. **Folding
  rewrites prose, so re-read the speaking characters' voice lines off their
  assets and keep them verbatim** (`../film-scenes/references/PROMPT-BLOCKS.md`)
  — a reworded voice line returns a different voice, and the same applies to a
  new shot card ordered from here.
- **Text that belongs to the world goes on as a layer here, never into a
  prompt (2026-09-11).** A headline on a screen, a number on a phone, a name on
  a door, a sign in the street: the model glitches typographic text, so the shot
  is generated without it and the words are composited over the clip, sized and
  placed to sit in the frame as though they were always there. This is the same
  rule `film-finishing` applies to the film's own titles — extended to text
  inside the picture, which it never covered. The picture prompts ban invented
  lettering for the same reason
  (`../film-scenes/SKILL.md`).
- **Keep dialogue and ambience/music on separate audio layers** in the scene
  graph — that separation is what makes J/L cuts possible (sound leading or
  trailing the picture across a seam) and lets the ambience bed carry across
  cuts, the edit-side counterpart of the prompt's audio-tail rule.
- **Speech-cut review on assembled dialogue scenes** (when the classic Picsart
  plugin is installed): `video-transcribe-audio` the assembled cut and read
  the reconstructed script AS PROSE — seams must read fluently, no dangling
  connective or orphan pronoun across a join, no clipped word at a scene
  edge. If it doesn't hold together as a paragraph it will not hold as a
  scene. Without that plugin: read the cut by ear and say so.
- **Virtual camera moves — the free camera adjustment.** The compositor can
  animate the clip layer's own window: either the **`ken_burns` motion preset**
  (`zoom` ≤ 1.2, `direction` `in`/`out`/a compass point, `pan` in composition
  pixels) via `picsart_media_apply_motion_preset`, or **`animations` keyframes**
  on `scale` / `position` / `anchor` via `picsart_media_patch_scene` — `anchor`
  is the zoom centre. A push-in is scale rising, a drift is position gliding, a
  punch-in is two scale keyframes ~0.1s apart. Exact shapes in
  `references/ASSEMBLY.md`. No new pixels —
  staging and validating are free, and the cost rides inside the export the
  film already buys. Use it when a good take has a dead camera, to recentre a
  drifted composition, or to Ken-Burns the animatic stills. **Honest
  limits**: 2D only (no parallax, no orbit — it reads as zoom, not dolly);
  keep zoom ≤ ~15–20% or the resolution loss shows (the finishing upscale
  masks some of it); nothing exists beyond the source frame, so a pull-out
  only works on takes generated wide (see `film-scenes`' generate-wide
  note). Wrong move or wrong energy is a camera-change retake in
  `film-scenes`, not a crop. The desperate last resort — a video-edit model
  re-rendering the clip with a different camera — is charged and risks the
  identity; name it as such if ever offered.
- **Verification is bounded: two fix rounds, then deliver.** For focused join
  experiments, use `references/JOIN-REPAIR.md`'s stopping rule instead; a rejected
  candidate never becomes the cut merely because two rounds elapsed. One round = look
  once (a scene review or a contact-sheet spot-check, not both), fix EVERY
  failing join in ONE rebuild. After the second round, export what you have
  and say in one line what is still weak — a flawed delivered cut beats a
  perfect undelivered one, and ending a session with no film is the worst
  outcome. This bounds the VERIFICATION loop only — a shot's 10–15 generation
  iterations in `film-scenes` are a different budget and keep their own rule.

## Cleanup — inside the one review (2026-09-09)

What generation spoiled — extra fingers and teeth, drifting objects, boiling
textures, edge jitter, random pseudo-text — is fixed when the user points at
it in the review. The repair path is the **edit sibling** (`film-scenes` step
5): the clip, the defect named to the second ("shot 3, 14–16s: five fingers
on the left hand"), the same numbered references — nothing else. Swap the fix
into the cut and watch the whole stretch in `picsart_scene_editor`, not only
the defect — an edit can fix the hand and lose the light. A defect the edit
sibling will not take in two tries is a re-roll of the run. If the model will
not give the shot back clean in 2–3 tries, the honest fallbacks are a recut
around it (a cutaway or a tighter trim — free) or accepting it, said out loud.
Frame-by-frame retouch is a real path only outside this surface.

Gate out: the user has called the cut final · shot order and trims saved to
`docs/` · everything durable on Drive. Route to `film-finishing`.

## Costs

| Free | Charged |
|---|---|
| bootstrap, re-bootstrap, `picsart_media_validate_scene`, `picsart_media_query_layout`, `picsart_media_probe_media`, `picsart_media_apply_scene_template`, **`picsart_media_export`**, `picsart_scene_editor` (the scene editor — playing the cut, and trimming, splitting, deleting and commenting on a shot are all free, happen in the browser and render nothing), `picsart_final_cut` **including its render** (the engine runs in the user's browser, so editing and exporting there both cost nothing — unlimited iteration), all planning | every cleanup regeneration (`picsart_generate`) |

The one charged thing here is a cleanup regeneration, and it routes through
`film-scenes` step 5 — so it carries that skill's rule: preflight, the number to
the user, then the yes. A fix nobody priced is still a purchase (2026-09-07).

Export is a real GPU render, so it costs time and makes a file — spend it on a
settled cut, not on every keystroke. It does not cost credits.

## Traps

- Editing only after all generation ends. The parallel edit is where coverage
  holes are found while they are still cheap.
- Cutting from the raw pile because selects lag. Fix the selects.
- Re-exporting to answer "what if". Export costs no credits, but every render
  takes real time and leaves another file in the user's Drive — re-bootstrap and
  the scene editor answer the question sooner and leave nothing behind.
- Re-exporting a Final Cut save through `picsart_media_export` "to make it real".
  It already is: the browser rendered it with the same engine. That render buys a
  second copy of a file you were handed.
- Opening the scene editor per clip, or once per piece after the user cuts in
  it. The editor is the whole cut, once — anything else is the same picture
  shown back several times, and only the first send gets answered.
- Locking your assembly after the user edited in Final Cut. The cut they saved is
  the cut; grading the wrong one strands finishing on a stale picture.
- Picture changes after lock without telling finishing.
- Grading before cleanup — the colorist grades shots that are about to be
  replaced.

Referenced files: 2

film-finishing11.8 KB

View saved version →

---
name: film-finishing
description: "Finish the film — colour unification and grade, the soundtrack decision (keep generation audio, add a music bed, or hand off to a sound team), the final-resolution pass, masters, and the project archive. Use for stages 9–11 of the film pipeline, after picture lock and cleanup. Routed from the film skill."
---

# ChatGPT and Codex integration

Apply these host conventions throughout this skill and its references.

- Discover the connected Picsart tools and use their actual names and schemas.
  The `picsart_*` names in the workflow identify tools, not a fixed host namespace.
  If the connection is missing, explain that Picsart must be connected; continue
  script and shot planning when possible. Never invent tool results or renders.
- Read board decisions from the current user message and attached widget context.
  Use `read_widget_context` only if the host actually exposes that tool. Do not
  assume ChatGPT has Claude's context reader. Treat a board's initial tool result
  and unsent widget state as previews, never as user approval. If a submitted
  decision has no accessible payload, explain briefly and use the text fallback
  to collect the missing decision; do not guess or repeat charged operations.
- Use the host's available question interface or one concise conversational
  question for preset choices.
- UI rendering and delivery of feedback are the remote MCP server's responsibility.
  If a board is unavailable, present its relevant options in text and record the
  user's selection using the same documented feedback fields. Do not claim that
  a plugin manifest makes Claude-specific widgets compatible with ChatGPT.
- Save `film.json` and artifacts in the user's writable project workspace, not
  the installed plugin directory. If persistent file tools are unavailable,
  keep a structured project checkpoint in the conversation and provide it for
  download when the host supports files. Never claim a file was saved without
  confirmation from a file tool.
- Honor the user's current instructions and already authorized scope. Preserve
  quoted spend limits and production locks; do not infer authorization from a
  preview, a timeout, or a connection being installed. A timed-out charged call
  needs a status check before considering a retry.
- This package holds six film skills (`film`, `film-development`, `film-assets`, `film-scenes`, `film-edit`, `film-finishing`) and three motion skills (`motion`, `motion-import`, `motion-design`). Route between them directly.
- `motion-import` reads a design's layers through the Figma app's tools. If Figma is not connected in this host, use its flat-frame fallback and say so once.


# Finishing — grade, sound, master, archive

Picture lock is behind you; nothing here changes the cut. The order inside this
skill is fixed and worth saying out loud to the user: **final-res re-roll →
re-assemble → grade → export once → masters → archive.** Grading the 480p draft
wastes the grade; exporting before the grade wastes an export.

For the paid final pass, follow `../film-scenes/SKILL.md`, **Native draft to
final**: use `fromDraft` only when this connection's live schema and preflight
accept it. Otherwise explicitly quote a regular final rerender and review the
changed picture before grading. Keep this plugin's existing export and delivery
flow; a draft is not a final-resolution deliverable.

## The final-resolution pass — pay once

The final quality comes from
**re-generating every approved shot at the final resolution with its saved final
prompt, verbatim** — same blocks, same references, higher `resolution`. Preflight
the whole batch, quote the total, and dispatch only on the user's yes; this is
the film's biggest single spend and the one that must never arrive as a
surprise (2026-09-07). Video upscaling exists on this surface; pick the upscaler
from the live catalog at the time of the pass (`picsart_model_catalog`,
`purpose: "utility"`) — none is named here, because the tools change. **The
practitioner post order is: upscale FIRST, then texture, then grade** — every
downstream correction should touch the highest-resolution pixels. Then
re-assemble the locked cut at the final canvas (≤1920×1920 —
multi-shot output cannot exceed it, whatever a single model renders).

Say the quiet part: this pass follows the approved prompts but is a **fresh
roll**. Small differences from the draft are expected and are not a bug. Where a
re-roll visibly breaks something the draft had (a face, a join), that is a Stage 8
regeneration on the final-res take — log it, fix it, move on.

## Stage 9 — colour: unify, then grade

Every generation arrives with its own baked-in grade, so the first job is
**unification** — bring neighbouring shots of each scene to one look — and only
then the creative grade. The creative tone already exists: it was baked into the
location assets in Stage 4, so the grade refines, never invents.

**There is no grade console.** It was retired on 2026-08-21 while the earlier
stages are being made right, and no grading panel exists yet. Until one does: resolve the real looks yourself with
`picsart_media_resolve_looks` (never a remembered look name), offer three or four
against the bible's palette line (or the locations' own colours with scheme
Auto) via the host's question interface, then apply. The mechanics
are unchanged — `picsart_media_apply_look` / adjust patches on the assembled
scene, free to set, charged only when a preview
(`picsart_media_contact_sheet`, per frame) or the export renders. Preview the
grade on a handful of frames from two or three scenes before approving it for the
film.

Scope order: per-scene unification first (`scope: "sc01"`), then the film-wide
look. **The anti-AI-look recipe** — the documented practitioner finish for
generated footage — layers in this order: upscale (above) → a film-grain /
fine-texture pass → the grade "until it reads closer to live action". Footage-
level grain at finishing is distinct from the style prefix's in-generation
grain: the prefix shapes what the model renders; this pass unifies what the
renders became. If the user has a real colorist instead, export ungraded and
hand off — see the package below.

## Stage 10 — sound: clean, bed, or hand off

What generation gave you: lip-synced dialogue in the shot, in-frame action noise,
some ambience. What it cannot give: consistent voice timbre between clips,
licensed music, a mix. The in-chat path:

- **Dialogue follows the film's voice policy** (`film.json.voicePolicy`, always
  `native` — there is nothing to decide, `film-development`). `native`: the generated voice is the performance — never
  re-recorded; between-clip timbre drift is a fact, and where it distracts,
  prefer a retake of the worse clip (Stage 8 path) over pretending a mix exists
  here.

  **The voice pass is parked (2026-08-27), and Stage 10 works with what the
  takes gave it.** The swap that keeps lip-sync needs a library `voiceId` and a
  designed voice cannot become one on this platform — the request is in
  `../../docs/ASK-VOICE-ID.md`; when it lands, the pass is: read the take against
  the script line, extract its audio, isolate it if there is room tone, convert
  it into the character's recorded voice, mux it back — same length out as in (±3ms
  measured), so nothing needs re-timing.

  Until then, sound at Stage 10 means the two things that ARE available: a
  continuous bed under each scene to hide the level and tone steps at every join,
  and honest level matching shot to shot. Do not offer a voice fix; say what the
  film has.

  Where a mouth is on screen for a whole line and even that is not tight enough,
  the shot can instead be generated audio-driven from the start
  (`ltx-2.3-a2v` — an audio track plus an optional first frame), so the lips
  follow our voice from the outset. Check the
  catalog for what is current rather than remembering a model id. Changing
  policy mid-film re-opens every dialogue shot; that is why it is decided at
  the lock.
- **Ambience is the glue**: when shots of a scene differ slightly in picture, a
  continuous bed stitches them into one space. Generation audio is per-clip, so
  seams are audible; note where, and lean on the music bed to carry across them.
- **Music bed — offered ONCE, only after the cut is picked.** Scoring a cut
  that is about to change wastes the track, and a declined offer is settled for
  the session. Don't interrogate ("do you want music?") — draft the brief from
  what the film already knows (register from the bible, tempo from the cut) and
  offer three exits: generate it, tweak the vibe, or skip.
  `picsart_generate` with a music model from the live catalog
  (`picsart_model_catalog`, `mode: "audio"`, `inputType: "music"`), and
  **instrumental always under speech** — vocals fight the dialogue for the same
  perceptual channel and both lose. Mix as a separate compositor audio track,
  dialogue dominant (duck the bed under lines; at full level only when the film
  has no speech). Flag plainly that generated music's licensing is the user's
  to verify for festivals/commercial use.
- **Sound post is a team's job**: a film going to festivals or dubbing needs a
  sound post team; offer the handoff package instead of imitating one.

**Handoff package** (either stage, when a human colorist/sound team exists): the
clean cut + per-shot source URLs from selects at max quality, the shot order and
trims from `docs/`, the bible (tone, colour scheme and palette line), the sound references gathered
with the film's references, and the generation audio as-is. Everything already durable on Drive —
that is the handoff.

## Stage 11 — master, QC, archive

QC on the full final export, ideally by fresh eyes, per the checklist in
`references/DELIVERY.md` (artifacts, joins, digital errors, titles — titles are
made in an editor or as overlay tracks, never generated; models cannot hold clean
typographic text). Then the deliverables and the archive — the full table and
list live in `DELIVERY.md`. The archive is not sentimental: prompts + registry +
log are **the means of production**; a sequel or a re-cut regenerates from them.

**The disclosure pack is a deliverable, not a note.** 2026 festivals (SXSW,
Tribeca, the AI-specific festivals) require disclosure of the AI tools and
models used — incomplete disclosure disqualifies at some. Write
`docs/DISCLOSURE.md` mechanically from the generation log: every model used,
per stage (images, video, voices, music), plus the human-contribution
statement and an AI-credits block for the titles. It costs nothing and the
film cannot be submitted without it.

Gate out (the film is done): QC notes closed · masters exported and checked by
playback · archive on Drive, complete · `DISCLOSURE.md` written · rights notes
(voices, music, AI-use disclosures where a platform requires them) gathered in
one place.

## Costs

| Free | Charged |
|---|---|
| look resolution, grade setting, scene re-bootstrap, planning, handoff assembly, every `picsart_media_export` (final and each master variant — render time and a file, **not credits**, 2026-09-07) | the final-res re-roll (every shot, video rates), grade previews (`picsart_media_contact_sheet`, per frame), music generation |

## Traps

- Grading before the final-res re-roll — grade the final pixels, not the draft's.
- Calling the final pass an upscale. It is a fresh roll; say so before it runs.
- Re-rolling the film at final resolution on the strength of the original
  forecast. Quote this batch, then wait — it is the biggest single number in
  the project.
- "One more export" per master variant nobody asked for. Exports cost no
  credits, but each is a real render and another file in the user's Drive —
  confirm the deliverable list first.
- Shipping generated music to a festival without the licensing caveat in writing.
- Archiving the film but not the prompts and registry — the film without its
  means of production cannot be revised or continued.

Referenced files: 1

film-scenes110 KB

View saved version →

---
name: film-scenes
description: "Generate the film's shots — the shooting period. Final per-scene shotlists, preset-driven shot design (camera moves, optics, two-source lighting, acting), every scene's pictures on one board (scene picture, wide, a room map when needed), the handbook's two-part prompt — one prompt per RUN of shots (as much of the film as fits a single generation, scenes included), the model making the cuts inside it — drafts, surgical iterations with a full log, the delivered run split back into its shots, and take selection into selects. Use for stage 6 of the film pipeline, only after the scenes' asset sheets are approved. Routed from the film skill."
---

# ChatGPT and Codex integration

Apply these host conventions throughout this skill and its references.

- Discover the connected Picsart tools and use their actual names and schemas.
  The `picsart_*` names in the workflow identify tools, not a fixed host namespace.
  If the connection is missing, explain that Picsart must be connected; continue
  script and shot planning when possible. Never invent tool results or renders.
- Read board decisions from the current user message and attached widget context.
  Use `read_widget_context` only if the host actually exposes that tool. Do not
  assume ChatGPT has Claude's context reader. Treat a board's initial tool result
  and unsent widget state as previews, never as user approval. If a submitted
  decision has no accessible payload, explain briefly and use the text fallback
  to collect the missing decision; do not guess or repeat charged operations.
- Use the host's available question interface or one concise conversational
  question for preset choices.
- UI rendering and delivery of feedback are the remote MCP server's responsibility.
  If a board is unavailable, present its relevant options in text and record the
  user's selection using the same documented feedback fields. Do not claim that
  a plugin manifest makes Claude-specific widgets compatible with ChatGPT.
- Save `film.json` and artifacts in the user's writable project workspace, not
  the installed plugin directory. If persistent file tools are unavailable,
  keep a structured project checkpoint in the conversation and provide it for
  download when the host supports files. Never claim a file was saved without
  confirmation from a file tool.
- Honor the user's current instructions and already authorized scope. Preserve
  quoted spend limits and production locks; do not infer authorization from a
  preview, a timeout, or a connection being installed. A timed-out charged call
  needs a status check before considering a retry.
- This package holds six film skills (`film`, `film-development`, `film-assets`, `film-scenes`, `film-edit`, `film-finishing`) and three motion skills (`motion`, `motion-import`, `motion-design`). Route between them directly.
- `motion-import` reads a design's layers through the Figma app's tools. If Figma is not connected in this host, use its flat-frame fallback and say so once.


# Scenes — the shooting period

This is where the money goes, so it runs as a seven-step conveyor per scene, in
order, because each step is more expensive than the one before it. The gate behind
you is real: **no run generates until the assets of every scene it spans are
locked** (the matrix in `film`) — a run that crosses a production-block boundary
waits for both blocks, so where the film works block by block, the run pass cuts
runs at block boundaries. The gate ahead: every shot `final`, in selects, logged.

**Nothing charged in video goes out on a number the user has not seen and said
yes to (2026-09-07).** Reported from real sessions: runs dispatched with no
credit figure in front of the user at all. Video is the film's largest spend, so
every charged video call — the pilot run, the batch, a re-roll, an edit, an
extend, the final-resolution pass — happens in three steps in this order:
`picsart_preflight` (free) returns a number, the number goes to the user in one
line, and the dispatch **waits for their yes**. One quote per dispatch, never per
shot: a run of five shots is one number and one yes.

What does NOT count as that yes — the forecast below (a plan, not a purchase),
the shot list's approval (that is the list's yes, and its board figure prices
per-card generations, which a run is not), the sheets' lock, the yes that opened
this stage, and the yes that covered an earlier dispatch. **Express pace changes nothing here.** When
preflight cannot price the call, say so in the same line — the rate arithmetic,
named an estimate — and still wait; an unpriced call never reads as free. Record
the figure in `film.json`'s `credits.nextActionQuote` before the call, so the
yes has a number on record.

**Before the money phase, state the film's forecast once.** Convert the
shotlist into numbers before anything renders: runs × expected takes × each
run's `genSec` × the model's per-second rate, plus a regeneration buffer
(~20%), the edit passes you can foresee (one per risky stretch, at the edit
model's rate — quote it once), and the charged QC
frames (take QC, cut-point checks). **The
~25% keep rate practitioners quote is per-shot data and does not transfer**: a
run passes only when every cut in it passes, and nobody has measured that rate
on this pipeline yet — so plan two takes per run, say the number is a guess,
and correct it out loud after the first run lands. One honest range — "this
film will cost roughly X–Y credits" — before the first dispatch; track
actual-vs-forecast in `film.json`'s `credits` and say so when the pace will
blow it.

## The unit of generation is the run, not the shot (2026-09-03)

**Repair exception:** this section governs initial production, not permission
to replace accepted footage. A user-approved connecting insert between existing
clips may generate alone under `../film-edit/references/JOIN-REPAIR.md`. Preserve
both originals and verify both joins; do not merge the insert into their run and
re-roll it merely to satisfy the packing rule.

**Shots are authored one at a time and bought together.** Measured on this
pipeline's own material: a film generated shot by shot — one `picsart_generate`
per card, the takes cut together at the edit — loses to the same shots
generated as ONE multi-shot take with the cards written in as its cut list. The
loss is not subtle and it is not one thing: identity holds across the cuts
because the model never re-reads the descriptor from cold, the light and grade
are less likely to step at every join, voices face fewer independent generation
decisions, and the cuts *land* — a match on action that one generation authored
into both halves actually matches, where two independent renders only ever
approximated it. *(Owed beside this date when the experiment's notes are typed
up: the model, the shot count, the run length, and the axes it won on. The
finding stands; the numbers are still to be recorded.)*

So the **run** is the unit: **as much of the film as fits inside a single
generation at the model's live ceiling** (`picsart_model_params` — Seedance 2.5
goes to 30s), scenes included. A short that fits is ONE generation — garage
scene and street scene together; a film that does not is the fewest runs that
cover it, split where the last paragraph of this section says. A scene change
inside a run is a cut like any other: a shot block whose first frame is the new
place, with the new scene's geography line stated where its first shot is
(`references/PROMPT-BLOCKS.md`).

**“Fewest runs” is a quality rule, not merely a cost preference.** The measured
continuity advantage above increases with the amount of consecutive film the
model authors together. Therefore a payoff, climax, close-up, long hold,
different emotional beat, scene boundary, or a belief that a shot will “hold up
better alone” is **never** a reason to start another generation. Those facts
shape the shot header and its seconds; they do not shape the run boundary. A
partition is invalid if the same ordered shots can fit in fewer runs at the live
ceiling while keeping every necessary run edge valid. Before dispatch, merge
every adjacent pair of planned runs that fits as one generation; scene changes
inside the merged run remain ordinary internal cuts.

Nothing about *authoring* changes. The shot cards, the cut-by-cut pass,
sizes, who is in frame, the geography line, the silence after every line — all
of it is still written per shot; the user still sees short cards on
`picsart_shotlist_board` and still sets each one with the presets. What
changes is where it lands: one prompt per run in the handbook's two-part shape
— a setup once (the tagging header, the tone, the camera line, the cut list),
then one short block per shot that opens with its shot type and camera move
(`references/PROMPT-BLOCKS.md`, 2026-09-09).

**Everything travels as a reference image; the prompt says what each one is
for.** The `startFrame` *field* and reference media are exclusive (step 2), and
that field anchors exactly one frame — the first — while a run has many shots
that each need identity pixels. The way round it is solved (user-verified
2026-09-03): the run's call carries `imageUrls` only — the **views** of every
character and location the run uses *and* every approved still — in a fixed
order, and
**the tagging header gives each image its role in words**: a face and an
outfit (*"100% matched based exactly on IMAGE n"*), `START FRAME` (the run
opens on exactly this image), `FRAME REFERENCE for shot N` (that shot opens on
this framing), `SPACE` (the wide, the map). The model honours the role it is
told; after the header, names only. That is the anchoring a run gets — identity
pixels for every character in every shot, *and* a photographed first frame for
any cut that has one. A character the user supplied as a **photograph** goes
first and untouched — *"Cal face 100% matched based exactly on IMAGE 1 — a
photograph of the real person"* — with one view of its generated angle sheet, when one
exists, second as *wardrobe and angles, face from IMAGE 1* (`film-assets`, *When
the user brings the asset*).

**One image, one thing — never a multi-panel sheet (2026-09-07).** Every entry
in `imageUrls` shows exactly one view of exactly one asset. An asset's sheet is
cut into single-view files at lock, and those are what a run carries; the sheet
itself stays in the library as the approved archive. This is not tidiness: a
four-panel reference of one woman at four ages, with the references header correctly naming
*"the adult-formalwear panel"*, gave sc04a the wrong Alina — and the identical
prompt gave sc04b a *different* wrong Alina, plus one frame that blended two
panels into a face not in the cast. Each generation is an independent draw
against the same ambiguity, so it fails differently every time, never appears in
a prompt diff, and holds *within* a run while moving *between* runs. Panel
labels rendered into the sheet's own pixels did not fix it either. **There is no
fallback pointer**: a prompt naming a panel or a position is a dispatch error
(the lint in step 2). And every run that needs a state passes the **same view
file**, so identity rests on byte-identical input rather than on a guess
repeating. The method and the crop recipe are in
`../film-assets/references/ASSET-SHEETS.md`, *How an asset reaches the run*.

**What you are accepting, stated once so nobody rediscovers it mid-film:**

- **A repair is an edit or an extend before it is ever a re-roll (2026-09-04).**
  The model ships two siblings for exactly this, recorded at the pick as
  `shoot.editModel` and `shoot.extendModel` (never a version named here —
  2026-09-10): the **edit** sibling
  changes something *inside* a clip (the clip + the change in words + the same
  numbered references → the same clip, changed), and the **extend** sibling
  makes a clip *longer* (the clip + what happens next → the clip continued).
  The note's *meaning*, read against what was selected on the board, routes
  the call (step 5): a note about how the marked stretch plays → the edit
  model; a note asking for something that has not happened yet in the clip →
  the extend model; only "regenerate / redo", or a note about the whole run →
  a fresh roll. A clip selected with the note "expand" is an extend call, never
  a regeneration — that mistake was made and reported. Both siblings are
  candidates, not measured methods: the first of each is a test, judged in the
  cut in the scene editor, and a clip that will not take an edit in two tries
  is re-rolled.
- **Handles exist only at the run's two ends.** Inside the run the model's cut
  is the cut; the trim room at an internal boundary is the silence after a line, and
  nothing else.
- **The declared cut times are a request.** The model cuts near them, not on
  them, and will occasionally fold two declared cuts into one or add one nobody
  asked for. The delivered take is split where it *actually* cut (step 6), never
  at the planned times, and a wrong cut count is a failed take.
- **Length can cost resolution on some models.** A model whose long takes cap
  below the finish format makes every run a resolution decision — which is why
  native multi-shot *at the finish resolution* is a hard filter at the model
  pick (`film`, model policy), not a trade-off discussed per scene.

**What you are not losing — and say so when it comes up:** per-shot *editing*.
The run comes back as one file and is split at its delivered cuts into
shot-shaped clips before anyone sees it — compositor bookkeeping, free — so
trims, reorders, drops, the scene editor's clips, Final Cut and the join QC all
stay per shot exactly as before. "We lose shot editing" is imprecise: a shot
is edited in place with the edit model, lengthened with the extend model, and
only re-rolled — with its run — when the user says so.

**There are no exceptions at planning.** No shot shoots alone, no film shoots
shot by shot: a risky stretch is a risk the run carries, a shot whose
composition matters gets a longer header and a labelled composition reference.
*Repair* is the edit model, the extend model, or a re-roll of the run (step 5).
The planning decision was made once (2026-09-03) so it is not re-made per
scene.

**One primary action per CUT is still the rule** — it moved from the
generation to the cut header. A header with five unrelated beats smears exactly
as a five-beat shot did; a scene of eleven cards is eleven headers of one action
each, with beats inside a header legitimate at roughly one per 2–3 seconds when
they are one continuous movement (a hand reaching, a head turning, a line
landing). What the run retires is the *other* half of the old rule — "use the
edit, not a generation, to build sequence structure" — because the measured
result says the generation builds it better.

**The ceiling is a packing limit, not a shot-length recommendation.** Author
coverage first, then pack those cards into a run. Default dramatic cuts are
3–8s and action may be shorter. A card over 8s needs
`design.durationReason`; over 12s is a deliberate long take. A scene longer
than 12s with one card must not pass silently: recommend a 2–4-shot coverage
version, warn that the long take
risks stretched motion and gives the edit no reaction/insert coverage, and keep
the one-card version only after the user explicitly chooses it. Model choice,
run grouping, visual rhyme, "restrained" tone, or spare seconds under the ceiling
do not constitute that choice. Several short shots still cost one generation
when they share a run — never merge their actions onto one card to save calls.

**Run length is arithmetic, not habit.** A run's `genSec` is the sum of its
shots' `sec` plus one handle at each end of the run, snapped **up** to a length
the model offers; the 3–8 seconds AI motion tolerates before it reads uncanny
(`film-edit`) is authored into each shot's `sec` rather than trimmed out of a
long take afterwards. Going to the ceiling is not an exception any more — it is
what a film costs — and the one trade-off that survives is the re-roll: a broken
second 22 of 30 re-rolls all 30, and the film has chosen to pay it.

Where the film outruns the ceiling, **the edge between two videos is joined the
handbook's way** (`references/SEAMS.md`, *The video edge*, 2026-09-09): video A's
last shot ends in the middle of a movement, video B's first shot picks that
movement up from a clearly different angle and size — a close-up, a detail, an
over-the-shoulder, never the same framing twice — and both videos carry the
same pictures in the same order, so every video dispatches together. The edge
falls where the ceiling falls, moved to the nearest moment where something is
moving; the story decides where scenes change, and an edge is never dragged to
a scene change to make it "clean" — that rule (2026-09-04 to 2026-09-08) made
films jump between places for no story reason. The one exception is a video
that changed its place or its people: the videos after it there take their
pictures of that place from its frames and wait for it (step 6).
(The extend sibling stitches several clips into one continuous video — ten on
the catalog's Seedance on 2026-09-10 — and may turn out to be a join between
two runs; nobody has measured it here — a candidate, not a method.)

**Inside a video the cuts are the model's, and the pair rules are written into
the shot blocks** — match on action, eyeline, screen direction, motivation
(`references/SEAMS.md`, *Internal cuts*). Three habits carry: the scene picture
opens the video — **the FIRST video of the scene only; a second video of the
same scene carries no `START FRAME` at all: it opens on its block's words plus
the identity views, the wide and the map** (`references/SEAMS.md`, *No two
adjacent cuts open on the same still*, and the reason it is a pixel contract);
one geography line,
identical in every prompt of the scene;
silence after every spoken line. Two uses of a frame image are opposites: an
**authored, approved still** labelled `START FRAME` in `imageUrls` is the
anchor (step 3); a **frame exported from an earlier video** is used only when
that video changed the place (step 6) or the scene is written as one unbroken
shot — it inherits the earlier video's drift and makes the next video wait.

## Which surface owns which decision

Every board answers about the artifact in front of it. Asking a question on a
surface that has nothing to show is what makes a console feel like a form.

| What exists | Surface | What the user decides there |
|---|---|---|
| nothing — a list | `picsart_shotlist_board` | story, coverage, shot **size**, **duration**, order, cuts |
| one run's take, split into its shots | `picsart_scene_editor` | is this the run, which cut (if any) failed, and what exactly is wrong |
| an assembled cut | `picsart_scene_editor` | watch it, trim, split, delete a stretch, and say in comments what to regenerate |
| a retake of a shot they already watched | `picsart_scene_editor` | the new take in the cut — keep it, or go back to the one in the log |

The shotlist board has **no modes**. It had a `plan` and a `conveyor` pass once;
the conveyor was take triage, and that moved off the board when it was rebuilt
(2026-08-21). The board plans — one call, every scene of the pass, one approval.
A take is judged in the scene editor, always.

**The board still thinks in shots; generation thinks in runs.** Its rows,
`genSec` and "commit to video" are per shot, and it has no way to show which
rows share a generation — so the run grouping is said in chat when the list is
approved, and the board's per-shot `genSec` is read as "what this shot would
cost alone", never summed into the forecast. A run marker on the board is a
server-repo ask (`../../docs/PLATFORM-ASKS.md`), not something to fake with
row labels.

**Camera has one author.** The shot's card on the shotlist board is where it is
chosen — the move pill, previewed by the board's own clip for that move — and
after a video exists it is re-asked exactly once, as the step-4 severity
routing. There is no separate design screen (retired 2026-08-21); a second
surface asking the same questions is the duplication this flow removed. Pass
takes with `kind` (`video` | `image`) so a still is never shown as a broken
player.

**The model is chosen BEFORE the durations.** A shot's length is only real if the
model accepts it — and what the model accepts is read off `picsart_model_params`
(`duration`: min, max, step), never from a note here: on 2026-09-10 the schema
said any whole second from 4 to 30, and a 7-second call was accepted and
delivered at 7.0 s — the older note in this file that listed a ladder with no 7
was wrong. The run's `genSec` is still
preflighted before the board opens. A card planned
model-blind gets snapped afterwards, which is an approved
plan being quietly changed. Pass `model` on the `picsart_shotlist_board` call and
the board offers exactly that model's lengths; a length nobody was offered cannot
be snapped. Switching models later re-opens every duration, which is why the
pick sits in Stage 1 — after the look console, before the shot list ever opens
(`film-development`) — and not at the bible lock. **And the model is chosen
before the runs**, for the same reason one level up: its ceiling is what decides
how many generations the film is, so a film grouped model-blind is a grouping
that will be redone.

## Step 1 — final scene shotlist

Sharpen the preliminary cards from `film-development` into the generation
shotlist. Per shot: id (`12A`), size, movement, FOV anchor, action (one),
line/sound, light/time of day, palette world, duration, and **the tags of every
asset in frame** — that last column is the prompt's checklist. The board's return replaces
the fields the board owns and leaves `design`, `refs`, `run` and `trim` on the
card by id (`../film/references/WIDGET-FEEDBACK.md`); an action that no longer
tells the story's paragraph is a story note back to `film-development`: the
paragraph is rewritten and shown first, never answered with a board.
Coverage logic as
on a set: scene master → sizes → details; mind eye-lines, the action axis,
cutaways. Present via `picsart_shotlist_board`, feedback per
`../film/references/WIDGET-FEEDBACK.md`. The markdown-table fallback is for a
connection that does not expose the tool — never a shortcut when it does, and
never the answer to "show me the shot list". **Asked to see the shot list, open
the board again**, whatever stage the film is at: the board is where a shot list
is read and changed, and printing the rows instead hands back data the user then
has no way to edit.

### The cut-by-cut pass — write every cut as a pair, then read three checks

Shots are drafted one at a time, as a director drafts them. Then walk the scene
in adjacent pairs, **before any prompt is assembled**, and write each cut into
the two shot blocks as a pair — the end of A's action and the start of B's —
with one of the four pair rules in `references/SEAMS.md` (match on action,
eyeline, screen direction, motivation). Nothing is recorded per boundary; the
pair lives in the prompt. A cut rule is a claim about two shots' *content*, and
content is decided in the prompt: an editor handed two shots that never shared
a movement cannot invent one.

**Three checks, free, read off the cards:**

- **No two adjacent shots of the same subject share a size.** Two sizes apart or
  the cut stutters. This is the checkable half of the 30° rule.
- **Eyelines agree.** Where a look answers a thing, A's gaze direction and B's
  subject placement are on opposite sides of frame. Same side reads as the
  thing being behind them.
- **Screen direction holds inside the scene**, shot to shot. A reversal is legal
  with a toward-or-away shot between it, and illegal silently.

Shot length is a habit, not a gate: dramatic shots run 3–8 seconds, action
shorter, a reaction shorter still (the handbook: *"Use the duration based on
what the shot needs."*). A shot over 8 seconds carries its reason in one line
on the card and nothing else — no approval, no alternative shown.

A failure here costs a keystroke. The same failure found after generation costs
a take.

### The run pass — the videos the plan named, filled to the ceiling

**The videos were decided in Stage 1, with the user's yes** — the video plan
(`../film-development/SKILL.md`, step 5) already says how many videos the film
is, which scenes each covers, its `genSec`, and one sentence for every join,
seeded into `film.json.runs[]`. The run pass does not plan videos; it fills
them. With the cuts written, walk the FILM in cut order and assign each card to
its planned video, **filling each one up to the model's live ceiling** (30 s on
Seedance 2.5; `picsart_model_params`). The edge falls where the plan put it —
on a movement, never a rest, never a scene change for its own sake
(`references/SEAMS.md`, *The video edge*). At that edge the last shot block says
*ends in the middle of the movement* and the next video's first shot block
says *picks that movement up from this new angle*, and is a clearly different
size and angle — a detail or an extreme close-up by preference.

**A moved edge is shown, never re-cut in silence (2026-09-09).** When the
cards' seconds do not sum to a video's `genSec`, or the plan's edge lands on a
rest, say so in one line with the fix — *"video 1 runs 33 s; the edge moves
from 1G to 1F, where Rasa's hand closes on the flask"* — and get the yes, the
way a story note gets one. Two adjacent planned videos that fit as one are one,
and that too is said, not done. On 2026-09-08 the videos were decided at this
stage, after the pictures and unseen, and the user asked four times how scene 1
connected to scene 2.

Record only what the dispatch needs: each shot carries `run` (`"r1"`), and
`film.json.runs[]` holds each video's shot ids in order (across scenes), its
planned `cutTimes` (the running sum of `sec`, in run seconds from the prompt's
0.0), its `genSec`, its `join` sentence from the plan, and its `refs` — the
ordered list of reference images every shot block numbers against.

Say the grouping in one line when the shot list is approved — *"the film is
three videos of 30 s as planned, the edges at 1F/2A and 2G/3A"* — because it is
the first thing that changes the invoice, and the one thing the board cannot
show.

### OTS is not a shot size — it is a two-body claim

Shot size is **one dial, and it means distance**: WS, MS, MCU, CU, ECU. Any of
them can be shot with one person alone in an empty room; none of them says who
else is present.

**Over-the-shoulder is a different kind of thing wearing the same label.** It
describes an arrangement: two people facing each other, the camera tucked behind
one of them, looking past their shoulder at the other. Recorded in the `size`
column it reads downstream as "how close is the camera", and three facts travel
nowhere:

- **whose shoulder** is in the foreground,
- **which side of frame** it sits on, and
- that a **second body must persist** — a close-up needs one person for six
  seconds; an OTS needs two people holding fixed positions for six seconds, one
  of whom is barely visible.

The third is what fails. A start frame where the near shoulder is an ambiguous
dark mass gives the model nothing to hold, and it stops treating that mass as a
person partway through the shot. Observed twice on one shot; it held only once
the near figure's ear, jaw, shoulder line and garment texture were all legible.

So the rules, and none of them costs a render:

1. **The card names the near body.** `values.nearCharacter` (whose shoulder) and
   `values.nearSide` (left/right). `picsart_shotlist_board` refuses an OTS card
   that names neither, and refuses one that lists fewer than two assets in frame.
2. **Check the side against the location map** before generating. The map already
   says who sits where; if it says Maro sits right, an OTS over her shoulder puts
   her on the right. Put her left and the audience reads it as her having got up
   and moved.
3. **The prompt claims the near body as a person, not a silhouette** — named
   features, and holding position for the whole shot. See the foreground-body
   clause in `references/PROMPT-BLOCKS.md`.

The board carries this as its own decision now — **Who's in it**, separate from
size: `single` · `two-shot` · `dirty single` · `OTS` · `POV` · `insert` · `group`.
Three of them (two-shot, dirty single, OTS) are two-body claims and take the
`nearCharacter` + `nearSide` treatment above. Two more were being mis-recorded as
sizes: a **POV** is about whose eyes the camera is — and the other actor must look
*into the lens*, which no size can say — and an **insert** is a thing rather than
a person, which is what an "ECU of the instrument on the table" actually is.
`group` is worth naming because three or more bodies is where models start
cloning people.

**Camera height is a decision too, and the board had none.** `eye level` ·
`low` · `high` · `top-down` · `dutch`. It is the second instruction a director
gives after size — low looks up and grants stature, high looks down and takes it
away — so a shot that never says it is a shot nobody framed. Set it per shot like
any other value.

An OTS also rarely travels alone: a conversation is covered as **two reverse
angles**, and the pair must match in everything except who is near — same lens,
same height, same distance, eyelines reversed. Nothing checks that pairing yet;
until it does, set the two shots' values identically by hand and look at them
side by side.

## Step 2 — design the shot on its card, then assemble the prompt

**The card is the design surface, and it arrives filled in.** Every shot's six
decisions — size, camera move, lens, light, colour, length — plus its
arrangement and camera height are pills on its card on `picsart_shotlist_board`,
each opening a picture rather than a list. **A pill the user changes is written
into that shot's block; a light left as seeded writes nothing, but lens and
aperture reach every shot, seeded or changed, and so does a palette other than
`auto`** (`references/PROMPT-BLOCKS.md`, Part 2, *The card's own picks*).
A choice made on the board is never a choice the model did not hear. **Seed them before the board opens**,
from what is already decided (the story's action, the scene location's light,
the world defaults' register, the run's model), so the user touches only what they
disagree with; nothing forces a stop, in either pace. There is no console behind
a row — the inline design panel and its "compose the first frame" button were
retired with the board's 2026-08-21 rebuild, and the requirement to open one
per scene went with them (2026-09-04). Without boards, the same rule: state the
seeded design in one line and the host's question interface only what the card leaves open,
from `references/PRESETS-CAMERA.md`, `PRESETS-LIGHTING.md`,
`PRESETS-ACTING.md`. Both paths return preset names plus their compiled text
fragments. World defaults come from the bible's setup; the shot stores only its
deviations (`design` on the shot in `film.json`).

**The scene's pictures are step 3, always (2026-09-08).** A shot's frame is no
longer composed here on request: every scene gets the handbook's set of
pictures before its run — the scene picture the run opens on, the wide, the
room map — on one board for all scenes (step 3).
A user who asks "show me how the opening looks" is shown that board.

**A fragment is the start of the Camera block, not the whole of it.** Nineteen
of the moves have been generated and measured (`genre-previews/_camera/` in the
plugin repo), and the finding that matters at prompt time is that a model
renders a described *picture* and ignores a stated *quantity*. So paste the
fragment, then make sure the block ends on the arrival as an image, name the set
as rigid if the camera travels through it, and — for any move that ends on a
changed aspect — say that the camera does the turning. `PRESETS-CAMERA.md`'s
"Getting a model to actually do the move" carries the rules and what each one
cost to learn.

**The board holds choices; the numbers behind them are yours.** Several blocks
want a quantity where the card holds a word — the Kelvin behind `warm`, the
km/h behind a moving car, the fog density behind a foggy street, the ratio
between two things of different size. **None of them becomes a pill.** The
console's job is a decision with a picture behind it, and nobody can judge 5200
against 5600 from a chip; a board that asks would be the form this pipeline
keeps trying not to be. Resolve each from what the user already chose plus the
scene's location and time of day, write it into its block, and do not raise it
in chat either unless it is genuinely in question. `PROMPT-BLOCKS.md` blocks 8,
9 and 10 carry the rules and the tables.

**A slow shot names which kind of slow it is.** There is no tempo setting
anywhere (retired 2026-09-04 — `PRESETS-LOOKS.md` has the why). Models render
described pictures and ignore stated qualities, so rhythm is written, never
stated: the shot block's action and the card's `sec` carry it, and tempo
words (slow, calm, measured, brisk, urgent) do not appear in a header. A shot
that should feel slow has less happening in more seconds, and it records
`design.slowReason`, from four:

| `slowReason` | What it is | How it reaches the shot |
|---|---|---|
| `anti-tempo` | slow *against* the scene around it, for contrast | real time; the neighbours carry the contrast, so it is a cut decision as much as a shot one |
| `hero reveal` | the thing itself deserves the seconds | **not generated slow** — generated at normal speed and ramped at the edit, because a model asked for slow motion smears instead of slowing |
| `breath` | the film needs air after a heavy beat | real time, minimal action, usually paired with a `tailHold` |
| `emotional pause` | a face working, nothing else happening | real time; the seconds go to the acting, not to the action lines |

The value is not decoration: three of the four are real-time shots whose slowness
comes from *having less happen*, and one is a post effect that should never be
asked of the generator. A card that just says "slow" gets whichever the model
guesses.

**Generate one size wide when the shot can afford it.** A take framed one
shot-size wider than the card's intent gives the edit spatial trim room —
virtual reframes, subtle push-ins, and composition fixes are then free crop
moves instead of retakes. Skip this on faces at CU and tighter (resolution is
the budget; a crop spends it).

**Generate one beat long, for the same reason.** Two numbers, not one:

- `sec` is the **cut length** — how long the shot runs in the film. It is what
  the ±10% runtime gate measures and what the dialogue rule computes.
  Unchanged.
- `genSec` is the **generation length**. For a run it is the sum of its shots'
  `sec` plus a handle at each end *of the run*, snapped **up** to a duration the
  model actually offers; for a solo shot it is `sec` plus a handle at each end.
  It is what you pass to `picsart_generate`, and you trim back to the shots'
  `sec` in the assembly.

This is the temporal twin of shooting one size wide, and the same logic: a
generated take has no second take, so the cheapest repair is material you
already paid for. A tail that drifts, an action that lands half a beat late, an
end state the next shot cannot cut to — with handles those are trims; without
them they are re-renders.

**Handles are a per-film policy**, stored independently of setup feedback
(the MCP setup no longer sends `handles`), because
they are a real and permanent cost: duration is what video models bill. On a run
they are exactly two — one at each end of the run — and they are cheap in
proportion: two seconds on a 28s run, not two on every 5s shot. Skip them
(`handles: "off"`) only when the run is already at the model's ceiling and there
is no room to add. **Inside the run no shot has a handle**, by construction: an
internal boundary is the model's cut, and its only trim room is `tailHold` and
the audio line's silence. That is why the silence is not optional, and why a run whose
line runs into its cut is a failed take rather than a trim — there is no handle
behind the last word.

### Audio: the takes keep the voice they were born with (2026-08-27)

**Shoot dialogue with `generateAudio: true` and each speaker's saved voice
description.** Read the character's `voice` field and copy it verbatim into every
speaking block, including inserts, retakes, edits and extensions; see
`references/PROMPT-BLOCKS.md`. Fill a missing field once using
`../film-assets/references/ASSET-SHEETS.md`, without an extra user question.
Describe the shot's emotion and volume separately. Native audio is the default;
no separate TTS, cloning or saved audio sample is required for this text rule.

The description improves consistency but does not guarantee identical timbre.
Compare new dialogue with accepted takes before declaring the voice consistent.
Optional reference audio is conditioning, not a deterministic voice lock or a
promise that the supplied waveform will be reproduced. A separate voice workflow
is a distinct production choice; do not silently substitute it or remove dialogue.

Five rules make native audio survivable. They are not optional:

1. **Fewer, longer takes.** Keeping a dialogue scene in one generation reduces
   independent voice decisions. Still check the delivered performances; neither
   a shared run nor the copied description guarantees perfect consistency.
2. **Transcribe every dialogue take and read it against the story's lines.** The model
   paraphrases, drops words, adds filler, and mispronounces names. Cheap to
   check, impossible to fix later without a retake.
3. **Demand silence handles** — a beat before and after each line — and reject a
   take that runs its line into the last frame. Native audio is welded to the
   picture, so the only cut points the edit gets are the silences the model left.
4. **Carry a bed across every scene.** Each take is mastered on its own, so
   levels, EQ and room tone step at every join; a continuous ambience or music
   bed is what hides it. Audiences forgive picture mismatch, not level jumps.
5. **Preserve the scripted dialogue.** Voice drift is a review issue, not a reason
   to replace a requested line with silence or narration. Judge accent, register
   and timbre against the accepted character performance, separately from emotion.

A bed can smooth changes in room tone and level; it cannot repair a changed
speaker identity. Do not automatically accept voice drift under the native policy.

### Frames or references — one way: `imageUrls` only

**Every video call carries `imageUrls` only, and the `startFrame` field stays
empty** — on a run, a bridge, an insert, a one-shot reshoot, whatever the clip
is called (rule 7, `../film/SKILL.md`). The frame the video must open on is
picture 1, labelled `START FRAME` in the tagging header; the identity and
wardrobe views of everyone in the video ride beside it, in the order `runs[].refs`
fixes, and the header says which is `FRAME REFERENCE for shot N`, which a face
or an outfit, which `SPACE`; after the header, names only. User-verified
2026-09-03: the model honours the role stated in words, so one call gets both
things the two fields used to force a choice between.

Why the field is never filled — measured facts, kept here as reasons, not as
a second recipe:

- **A start frame and reference media cannot travel in the same request**, and
  "reference media" means images, video **and audio** alike: the video model
  routes on which fields you set. Verified by running it (2026-08-26) — a call
  carrying both `startFrame` and `audioUrls` came back

  > `bytedance_invalid_parameter` — *"The parameter `content` specified in the
  > request is not valid: first/last frame content cannot be mixed with reference
  > media content"*

  while the same audio alongside `imageUrls` rendered fine. Seedance 2.5's
  schema pairs `image_urls` (up to 30 references) against `image_with_roles`
  (≤ 2 entries: `first_frame`, `last_frame`, `reference_image`) as
  alternatives. Sending both is a rejection **after** the wait.
- **A frame shows only the people in it.** The 2026-09-08 bridge went out with
  scene 1's last frame in the `startFrame` field — one woman's face — and no
  picture of the other woman, who was in the shot but not in the frame. She
  came back in a long coat with two flasks, and the bridge put a wardrobe jump
  on both of its joins.
- **Native audio is NOT reference media.** Tested end to end (2026-08-26):
  `generateAudio: true` with the line in its shot block was accepted, rendered,
  and the character spoke the line — so a call carries its lines and its
  images together. Until 2026-09-09 this test was also written up here as a
  second way to build an anchored shot (*"approved start frame + generateAudio
  + the line"*). It is not a way: it is the proof about audio, and the call is
  still `imageUrls` only.

- **Keep the reference set tight, and every entry a single view.** The
  identity view and the wardrobe view of every character in the video — both,
  always (rule 7, `../film/SKILL.md`) — and per scene the run plays its pictures — the scene
  picture, the map and the wide (step 3). A tagged
  state a cut names — `@cal_wet` — is its
  own view, numbered like any other image, never a panel pointer into a sheet.
  For a photo-sourced character the photograph is the identity view and, when
  it shows the outfit the story dresses them in, the wardrobe view too; when it
  does not, the user is asked for one more photograph that does (rule 7,
  2026-09-10) — a view of its angle sheet comes along only when a cut in this
  run needs that angle. Every prop the film gave a plate to rides as `IMAGE n —
  PROP: <name>` (rule 7): the descriptor is not in the prompt, so a picture is
  the only carrier an object has. Every image is inlined and the total has a
  ceiling.
  - **The ceiling is real and it is measured before every dispatch**
    (2026-09-04). The 22MB that died on `inline_media_too_large` was three
    *lossless PNG* references; a view is a JPEG crop of a max-resolution sheet
    scaled to a **2048 px delivery cap** (the model renders 1080p and declares
    no input pixel limit, so the cap is ours to hold), which puts each one at a
    few hundred KB — **what binds is the image count, not the bytes**. Still: `picsart_media_probe_media` every
    URL (free — `bytes`, `width`, `height`), sum, and stay **under 15 MB** —
    the count follows the scenes the run plays (two characters and one scene
    are seven images; Seedance 2.5 takes 30) and the old 6–8 cap is gone
    (2026-09-08).

    **The probe returns `width` and `height` for a reason: read the short side
    of every picture, not only the bytes.** `min(width, height)` under 300 px is
    refused by the provider after the wait, so nothing under **320 px on its
    short side** is dispatched — width counts as much as height. On 2026-09-09 a
    252×508 wardrobe view was probed, its bytes were summed, and it was
    dispatched anyway: only the total was being read, and the floor everyone
    remembered was *"300 px tall"*. The cut that made it is fixed in
    `../film-assets/references/ASSET-SHEETS.md`, *The crop*; this is the gate
    that should have caught it regardless.

    Over budget, in order: **drop the wide — but only when the map is in the set
    and no cut in the run opens the setting up** (a pull-back, a reframe wider
    than the shot it came from, a move that reveals a skyline or a street: those
    cuts need the wide, and dropping it there is what let a location drift on
    2026-09-10); **the map never leaves, and neither do the identity
    and wardrobe views** (rules 7 and 10 — no descriptor is in
    the prompt any more, so neither the costume nor the geography has another
    carrier); re-encode any PNG as
    JPEG; downscale a view only as far as keeps
    the identity view ≥ 600 px on its short side — and **a view downscaled once
    becomes that state's file from then on**, recorded in `views[]` and passed
    by every later run, never a one-run copy (one state, one file). The old last step — splitting
    the face panel out of a sheet — is gone, because that split now happens at
    lock for every asset. The method and the export calls are in
    `../film-assets/references/ASSET-SHEETS.md`, *How an asset reaches the
    run*. Never discover the ceiling after the render.
  - **The panel-pointer lint, run at the same moment (2026-09-07).** Read the
    image labels before the prompt goes out — the references header and every cut header's
    image reference. A pointer **into a reference image** — *panel 2*, *the
    leftmost panel*, *the top-right of IMAGE 1*, *the bottom row of the sheet* —
    is a **dispatch error**, not a wording preference: it means one image in
    this set has more than one thing in it, and the fix is a crop (free, at
    boxes `film.json` already holds), never a reworded label. Two runs of one
    correct pointer returned two different women; the wording was never the
    variable. **Scope: reference labels only.** Position words describing the
    *frame being made* — the location map, the blocking block, where a
    character stands, where the camera is — are the film's staging and are
    required; the lint never touches them.

**The anchor is compositional, not pixel-exact.** Measured on that test: the
delivered first frame scored SSIM 0.73 / PSNR 20dB against the approved still —
same woman, wardrobe, room, pose and props, framed slightly wider with the
subject shifted. Excellent anchoring, and *not* a guarantee of identical pixels,
whatever the vendor doc's "exactly match" says. That measurement went through
the `startFrame` field; a run's frames travel in `imageUrls` with their role in
words, and the same expectation applies — compositional, not pixel-identical.

**Reference audio is a conditioning input, never the film's voice.** Also
measured: feeding a 5.3s scratch line as `audioUrls` produced audio whose
envelope correlated with it at 0.249 — the model performed its own line in its
own timing, not the supplied track. So a scratch mix earns its keep for
**timing** (see step 3), and the final voice comes from native audio or from the
dub at finishing. It is never delivered by attaching a wav.

**Duration = the spoken line + one second, snapped UP the model's list.** Block
11 already requires a second of silence after each line as the editor's cut
point; the duration has to leave room for it. A 5.3s line needs 6.3s, and a
solo clip snaps that **up** to the next length the model's `duration` schema
allows (read off `picsart_model_params` — never a ladder remembered from a
note; on Seedance 2.5's step-1 schema 6.3s is a 7s clip, and 7 s was accepted
and delivered on 2026-09-10). The same test
asked for 6s and the dialogue ran to the final frame with no cut point — exactly
the failure the audio line exists to prevent. **Inside a run the snap moves to the
total.** Cut times are prompt text, so a shot inside a run keeps its exact
`sec` — 6.3s is 6.3s, no ladder — and only the run's `genSec` snaps up to what
the model offers; the slack that snapping adds goes to the run's tail handle,
never to a shot.

Either error — the mixed pair, or `inline_media_too_large` — is permanent.
**Change the payload; never re-run the same one**, and say in one line what you
changed.

Every run's prompt is the handbook's two-part shape — see
`references/PROMPT-BLOCKS.md` for the template, the shape per model, a worked
example and the pre-dispatch checklist. The assembly is mechanical by design:
the handbook's lines verbatim, the style prefix verbatim from the bible, the
geography line verbatim from the scene; no descriptor is pasted — the pictures
carry identity. There is no negative prompt: every ban is written as what IS in
the frame.

**Before every video dispatch, including edits, extensions and inserts, run the
prompt-verify step — `references/PROMPT-VERIFY.md` — not just the format lint.**
It opens the files that govern a run (the blocks reference, the camera, lighting
and acting presets, the seams, the look), derives what they require for this
call, and checks the **literal tool arguments** against them; its verdict is
written before preflight and a fail blocks the dispatch. Use
`picsart_prompt_verify` for the objective half and read the rest yourself. A valid schema and a credit approval do not excuse missing dialogue,
changed story content, or a house line paraphrased on its way into the prompt.

**Price the model before believing the draft rule.** The rule below exists for
ONE reason — keep the iterations cheap so only the keeper is paid for at full
size — and it only works on a model whose price moves with resolution. Prices
change and no number is recorded here: quote both resolutions with
`picsart_preflight` before the first dispatch. If the price moves with
resolution, draft at 480p; if it is the same, draft at the finish resolution and
there is no second pass to pay for. Say which of the two the film is doing, once,
in the forecast.

### Native draft to final

Read `picsart_model_params` for the chosen model before quoting. On Seedance
2.5, when the schema lists `draft`, use `picsart_generate` with
`extra: { "draft": true }` for a native draft, keeping the approved prompt,
references, duration and format. Preflight the same payload including `extra`.
Record the returned result URL exactly in `film.json`.

If the live schema also exposes `fromDraft`, preflight a final request with
`extra: { "fromDraft": "<exact draft result URL>" }`; use the schema's
required envelope only. The tool requires a `prompt` string: use an empty
string for a from-draft request only if preflight accepts it. Re-quote and
obtain approval before the paid final. Do not add the original references,
duration or draft flag unless the live contract requires them.

If `fromDraft` is absent or rejected, it is not available on this connection.
Explain that the final requires a separately quoted rerender and may change
content; follow the normal finishing approval and re-review. Choose a regular
resolution-priced draft instead when that better fits the approved budget.

For models without the native draft path, dispatch drafts at **480p** — where resolution actually changes the price —
with the run's images in `imageUrls`, in exactly the order the prompt numbers
them, and **the `startFrame` field empty** — the start frame is the image the references header
labels `START FRAME`. `generateAudio: true` on any run with a
line in it — the line, its lip-sync, and
the voice block are part of what is being judged; a run with no speech drafts
with audio off. Audio follows the film's **voice policy** (always `native`,
recorded in `film.json.voicePolicy` — nothing to decide) — and *how the approved voice
reaches the picture* is settled before the first dialogue take, not at finishing:
see the next section. **Every video generation is submit-and-poll**:
`async: true` + `picsart_job_status` (the server defaults every media mode to
async now, so this holds even if you forget) — a synchronous video call dies at
the host's ~60s tool window while the render keeps running and charging, with
no handle left to recover the result. A host "isn't responding" error is therefore never a failed
generation: poll the job, never re-submit on that signal.
`picsart_preflight` the batch, **give the user the quoted total, and dispatch
only on their yes** (the law at the top of this skill) — surprise spend is the
one unrecoverable mistake.

After dispatching, put the batch's job handles on `picsart_render_monitor`
(when the server has it): it polls the jobs itself, shows live per-shot
progress, and **reports the finished takes back into the conversation
automatically**. Never ask the user to wait, check back, or "ping me in five
minutes" — that is the monitor's job. Without the monitor, poll
`picsart_job_status` yourself; if a render outlives the turn, say plainly
what's running and check it first thing next turn.

## Step 3 — the scene's pictures: one board for all scenes (2026-09-08)

Read `../film-assets/references/ASSET-EFFICIENCY.md` for scheduling and changes:
independent ready scene chains advance without waiting for unrelated retries;
an upstream correction triggers a content-impact check, not a blanket rebuild.
Scene pictures still use the full scene style prefix, unlike neutral asset plates.

**Every scene gets its pictures before its run, and nothing else is generated
between the shot list and the run.** Until 2026-09-08 stills were on request
and the run's only picture of a place was a clean, empty location panel. The
handbook does it the other way round, with a reason that stands: *the model is
trained on films with actors in them — an empty location renders unreal, so
make the scene first and take the location from it.* A scene's pictures are one
dependency graph: wide first, opening crop and optional map derived from it. They
are what the run's `imageUrls` carries for that scene — the scene picture as
`START FRAME`, the wide and the map as `SPACE` — so the model never has to
guess the room. A `FRAME REFERENCE for cut N` rides along only when a frame
exported from an already-approved run supplies it (step 6); none is authored
ahead of time. Character views ride beside them (step 2).

**Wide first, then cut the scene picture out of it, then the map — one approved batch (2026-09-23).** Two generations and one free crop:

1. **The wide is generated first**, at 4K, with the cast in it from their
   identity and wardrobe views; the scene-picture craft below (whose cast
   opens the scene, the prompt lines, the three planes) applies to it.
2. **The scene picture is cut out of the wide** with `picsart_media_export` (the crop
   recipe, `../film-assets/references/ASSET-SHEETS.md`, *The crop*): a box
   around the opening shot at the film's ratio, no credits, the run's `START
   FRAME`. Its short side must clear the video's resolution (1080 px for
   1080p), else the wide itself is the start frame. In a vertical film a
   two-shot barely zooms, so a scene opening on two usually starts on the wide.
3. **The room map is generated from the wide**, when needed (item 3 below).

Quote the wide and the map as one intended batch. Preflight each concrete
payload, obtain approval for its cost, and use `picsart_generate` asynchronously.
The map waits for the actual wide URL and its visual check; if its exact quote
could not be obtained in advance, show that quote before dispatch. The opening
crop is free to render. Present wide, crop and map together for one scene review.
A scene starts as soon as its own cast is approved. A request to shoot now can
authorize the prerequisite pictures and run together only when their scope and
cost are explicit; preserve all dependency and asset checks.

**The chain, per scene** (a scene = one place at one time of day; a scene that
moves rooms is two chains). **Model (2026-09-23): the wide on a model that
renders 4K (`gemini-3.1-flash-image` today, `resolution: "4K"`; check the enum
with `picsart_model_params`); the room map, square and empty, on the GPT Image
family — ids as recorded in `film.json` (`model.pictures` — `people`,
`map`), read off `picsart_model_catalog` once at Stage 4
(`../film/references/PIPELINE.md`, *Image models are families*).**
On *The Last Drop* (2026-09-08) Nano Banana lost faces in two-shots made from
whole sheets and Seedream held them; with cut views (2026-09-23) Nano Banana
held both. If a two-shot fails on faces again, try Seedream for that wide,
accept the smaller cut, and write the finding here. The
order below is the order things are attached, because the handbook's prompts
say *the 2nd photo*. Every prompt opens with the two film lines
(`../film-assets/references/ASSET-SHEETS.md`, *Every image prompt carries the
film*).

**Before either generation dispatches, run the prompt-verify step
(`references/PROMPT-VERIFY.md`, 2026-09-13) — it is mandatory for a still
exactly as it is for a run, and the still row of its governing-files table
names what to open.** A scene picture, a wide or a
map is still a generation with subjects tagged
in it, and the same failure the gate exists to catch on video happens on
stills just as easily: a character named in the shot but written into the
prompt only as the target of someone else's reaction, with no pose or emotion
of their own. Check the literal text about to be sent, not a recollection that
the rule is known.

1. **The scene picture** — the handbook's *base image*, the scene as its first
   frame. **CUT from the wide since 2026-09-23** (above): what follows is the
   craft of the wide's prompt and of where the cut falls. Every head in the cut
   at least a tenth of its frame tall.

   **"The scene as its first frame" means the cast of `shots[0].assets` —
   read it, never the scene's dramatic centre (2026-09-13).** A scene whose
   cast changes as it plays (someone arrives on a footsteps cue, a knock, a
   door) is *about* the pairing that arrives, but its FIRST shot is whoever
   is already in the room — and the scene picture is the first shot, not the
   scene's throughline. Built from the throughline instead, a three-person
   confrontation scene got its base image as the two-shot of the arriving
   character and the one already there, when the shot list's own `1A` named
   only the two who open it — the run would have opened on someone who has
   not entered yet. Before writing `Subject #1`/`#2`, open the approved shot
   list and copy `shots[0].assets` verbatim; if that list disagrees with
   whichever pairing feels like the scene's centre, the shot list wins.

   Prompt: the handbook's
   thirteen lines, in its order, each filled from the film — *describe only
   what the camera sees*:

   ```
   Visual tone: <bible.stylePrefix, verbatim>. Skin matte and textured with visible pores, no gloss or shine. A frame of live-action film — real people, real fabric, real light; not an illustration, not animation, not a 3D render.
   Subject description: <one distinguisher per present character — "Subject #1 — @cal, worn dark-blue work jacket"; the face comes from the attached view, not from words>
   Shot size: <the card's size fragment>
   Camera angle: <the card's angle fragment; "Eye-level." when the card is neutral>
   Composition: <the card's arrangement or composition fragment, or N/A>
   Pose & action: <the card's action — what each subject is physically doing in frame one>
   Emotion: <the card's feels line, per visible face>
   Gaze: <each visible face's gaze target — in the scene, never at camera>
   Positioning in the frame: <each subject's placement from the location map — left third, right third, centered>
   Visible / Not visible: <what must stay in frame, what may crop>
   Foreground, Midground, Background elements: <all three, named — what sits closest to the lens, the plane the subject is on, what falls away behind them>
   Setting: <the location descriptor's setting line — place, condition, time of day>
   Aspect ratio: <the film's ratio>
   ```

   **The skin clause rides on `Visual tone`, and is ours (2026-09-11).** The
   prefix compiles the camera (grain rides in its stock) and the lens family,
   and a camera fragment says how its stock renders skin *colour* — none of them ask
   for skin *texture*, which is the one instruction that most reliably fights
   the render look on faces (2026-09-07). It was in the video prompt's quality
   line and nowhere in the still that sets the film's look. It is appended to
   line one rather than added as a fourteenth line, because the thirteen are
   the handbook's, in its order, and the only deliberate departure from them is
   the aspect-ratio substitution below. **Where the light comes from is not
   asked for here, and that is now a decision, not an omission (decided
   against 2026-09-11).** Lighting is per-shot by decision (2026-09-07), so it
   cannot ride the prefix, and the handbook's thirteen have no lighting slot;
   a scene picture takes its light from the time of day on `Setting` and from
   the plate it was derived from. The course argues a named light source is
   the largest single realism lever there is, and it may well be right — the
   case is kept in `research/ai-video-bootcamp/ANALYSIS.md` §4.12. It was put
   to the user on 2026-09-11 and declined. Do not re-raise it unopened.

   **The still says what medium it is, and bans the others (2026-09-11).**
   Appended to `Visual tone` after the skin clause, and taken word for word
   from what the character sheets already carry, so the two describe the same
   film: *"A frame of live-action film — real people, real fabric, real light;
   not an illustration, not animation, not a 3D render."* A film set up as
   animated says its own medium instead — the line is whatever the film is,
   never a fixed string. Until now this guard existed only on the character
   sheets: the sheets refused the render look while the picture that sets the
   whole scene's look asked for nothing, and every frame of the scene is
   derived from that picture.

   **They go to the compiler as `space` (`foreground`, `midground`,
   `background`), the same in every prompt of the scene; it refuses a run or
   a scene picture without all three (2026-09-23).**

   **All three planes are named, and `N/A` is an argued exception (2026-09-12).**
   The line used to end *"or N/A"*, and an escape offered in a template is the
   value that gets written. The course's sharpest demonstration is a matched
   pair — same model, same subject, same place — where the only difference is
   whether a foreground, a midground and a background were written down, and the
   version without them renders the person pasted onto the backdrop like a
   sticker. The claim is that the flat look **is** the missing layers, not a
   model weakness, and it is the same reason the handbook's own location plate
   carries the three slots. So the default inverts: a frame nearly always has
   something close to the lens — a shoulder, a doorframe, a railing, steam, the
   edge of a table — and writing `N/A` means claiming it does not, which is
   rare enough to be worth a sentence in the card's notes.

   **This is the near plane only; it is not an instruction to fill the frame.**
   A foreground object sits close to the lens and slightly soft, and it frames
   rather than competes: one already in the location, never a prop invented for
   the shot, and never something that crosses a face. Where the camera travels,
   check it against the measured warning that a single large near object sweeps
   the whole frame on a lateral or orbital move and drags the subject with it
   (`references/PRESETS-CAMERA.md`, *If you are generating a start frame too*) —
   many small cues beat one big one. The `Atmosphere` presets in that same file
   are the other half of this: the planes give the eye somewhere to travel,
   atmosphere is what physically separates them.

   **Adapted from the Skool course's framing lesson, and not measured here**
   (`research/ai-video-bootcamp/ANALYSIS.md` §10.3). It is free — it changes no
   field and costs no call — which is why it is adopted on his demonstration
   rather than held for a probe of ours.

   **`negativePrompt`, where the image model's schema declares one.** These are
   all image-model work, so the guardrail the sheets already use applies here
   unchanged (`../film-assets/references/ASSET-SHEETS.md`): for live action, the
   medium ban — the words that rule out drawing, animation, rendering and
   plastic-looking skin. `picsart_film_compile_asset_prompt` returns it in
   `negativePrompt`; take it from there rather than writing it. Never as prose,
   never in a video block. The lettering ban below is scoped more narrowly than
   this one.

   **The lettering ban goes on the scene picture and the wide — NEVER on the
   room map.** The map's prompt asks in as many words for a label
   on each side, *A on the north side, B on the east side*, and those labels are
   the only reason the map exists: they are read off it to write the geography
   line that every prompt of the scene then carries. Banning lettering there
   destroys the artifact. On the three that do take it, the compiler adds the
   lettering ban — every form of on-image text, from signage to watermarks — to
   the medium ban above.

   **A film that needs a sign does NOT ask the model for it (corrected
   2026-09-11).** A story with a readable shop name, a door number, a headline
   on a screen or a figure on a phone still gets the ban — because the model
   cannot hold clean typographic text, which is the same reason
   `film-finishing` already refuses to generate the film's titles and makes
   them as overlay tracks instead. In-world text is the twin that rule went
   silent on. Keep the words out of the generation and **lay them in as a
   compositor layer over the finished clip** (`../film-edit/SKILL.md`),
   positioned so they read as part of the frame. The ban and the wanted text
   are not in conflict: the ban stops the model inventing lettering, and the
   layer supplies the lettering the film actually wants, clean.

   An earlier version of this note said to name the exact string on the
   *Visible* line and let the model render it. That was wrong, and the
   practitioner evidence is explicit — numbers rendered into a generated shot
   come back glitched, so they are typed out separately and laid over the clip
   afterwards, where nobody can tell they were not always there
   (`research/ai-video-bootcamp/ANALYSIS.md` §9).

   **Why the lettering ban is worth its own words.** The video prompt has
   carried the handbook's no-subtitles line on every generation for some time
   (`references/PROMPT-BLOCKS.md`, item 7). The still that every one of those
   videos is
   built from carried no equivalent, so a model was free to invent signage,
   labels and shopfront text in the picture, and the video then faithfully
   reproduced whatever it invented. This is the same shape as the medium guard
   above: a rule that held one end of the pipeline and went silent at the
   other. What it is aimed at is **invented** lettering — the garbled pseudo-text
   a model reaches for when it has to fill a sign it was never told about — and
   not at text the film actually wants, which is why it has the two exceptions
   above. That failure is not hypothetical: **the run QC below already rejects
   "random pseudo-text" as an artifact**, beside hands and teeth. We were
   catching it after a video had been paid for and rejecting the take; this
   catches it one step earlier, in the picture the take is built from.

   **The wide is the authored source picture.** Its compiler input carries
   the locked medium, skin treatment, lettering constraints and all three
   `space` planes. Its opening crop inherits those pixels. The map uses the
   checked wide as its reference and the compiler's map template. Preserve
   each compiler result's `negativePrompt` and matching `promptToken`.

   Attached: the identity view **and** the wardrobe view of every present
   character — both, always (rule 7, `../film/SKILL.md`) — and, for a real
   place, the user's photographs of it. **Nothing else**: no room map, no prop
   plate, no extra still. On 2026-09-08 five references went into a two-shot
   and the faces were outvoted; the roll that passed carried the faces only. A
   prop is named in words with its count on the *Visible* line (*"one flask,
   on a cross-body strap"*). The handbook writes 16:9 or 9:16 here; ours is
   the film's ratio — the one substitution. Recorded on the scene as
   `pictures.scene`. **Looked at as soon as the plan lands**: each face
   against its identity view on the preview URL (`film` rule 4);
   a wide with one wrong face is regenerated, and its cut and map with it —
   on 2026-09-08 three derived frames inherited one wrong face. **Two failures on faces in a row change
   the reference set or the model, never a third roll of the same call.**
2. **The wide — generated FIRST since 2026-09-23**, from the cast's views and
   item 1's craft, at the film's ratio and 4K.
   `pictures.wide`.
3. **The room map — only when the scene needs one (2026-09-09)**: an
   interior, or any scene whose cards cut between two or more walls behind the
   people; a single outdoor place with one background gets no map, its wide is
   the geography (two of three maps came back wrong on 2026-09-08 for a square
   that needed none). When needed, from the wide — the handbook's *location
   sheet*. Its prompt asks for a top-down view of the place in the photo with
   every corner and side visible, no people in it, each side lettered A to D
   clockwise from north, the colour and realism of the source kept, 1:1.
   `picsart_film_compile_prompt` pastes it (a typo in the handbook's own
   wording, "one" for "on", is corrected there, once). Attached: the wide. **One map per place and time of day**,
   made from the first scene played there and reused by every later scene at
   that place — the handbook makes one per scene; one per place is what keeps
   the walls the same across scenes, and it is ours, deliberately. When the map
   lands, read it and write its four sides into the location map (the geography line):
   *"Side A — the shelving wall; Side B — the roller door; …"*. Recorded on the
   location as its `map` view.

   **Left and right are read off the scene picture, not off the map
   (2026-09-09).** Who is frame-left and who is frame-right in the scene
   picture goes into the geography line, and the sides never change inside a
   scene (`references/SEAMS.md`, screen direction). The map says which wall is
   behind each face — **the background side is the wall behind the face of the
   person facing the camera**, read off the map, never copied from an example.
   On 2026-09-08 a reverse was written with Nen's shoulder frame-right while
   the master had her frame-left, and cost a round. **What a reverse changes**:
   whose back is in the foreground, the background wall, and the pointing hand
   becomes the other hand — say it in the shot block (*"Subject #2 hand
   pointing should be the other hand"*), because the model confuses hands
   across a reverse.

**Check each picture against the one before it as results land** — with
`picsart_view_image` on the preview URL (`film` rule 4), beside
showing it (`film-assets`, *Show it the moment it lands*) — the same faces as the
identity views, the same grain and colour, the same room — and re-dispatch on
drift. No picture becomes a start frame or a reference until its check is
back. On the
wide, the handbook's questions: did the room stay the same room; did the
characters keep their left and right; is anyone looking at the camera.

**When a picture comes back wrong, read it back to the line that made it
(2026-09-13).** The rule above says how many times to try — two failures on
faces in a row change the reference set or the model, never a third roll of the
same call. It does not say *what to change*, so a picture that came back plastic
and a picture that came back generic have been getting the same treatment: the
same prompt, rolled again. Every roll is a charged generation. Read the defect
back to the line of the thirteen that owns it, change that line, and only then
re-dispatch.

| What came back | The line that owns it | What to change |
|---|---|---|
| the person could be anyone; the face is not theirs | `Subject description`, and the attachments | the exclusive tell is missing or is not exclusive; or the identity and wardrobe views were not both attached (rule 7) |
| the whole frame reads as a render; skin is plastic | `Visual tone`, and `negativePrompt` | check the skin clause and the medium line are on line one **verbatim**, and that the ban field was actually filled. If both were there, this is not a wording problem — it is the reference set or the model |
| the place is a stock backdrop; it is nowhere in particular | `Setting` | thin setting line, or the location's anchor object was never named — a place without its anchor is "somewhere", and somewhere drifts |
| the people look pasted onto the background | `Foreground, Midground, Background elements` | a plane was left unnamed — most often the near one. This is the defect that line exists to prevent |
| the frame is inert; everyone is centred | `Positioning in the frame`, `Composition` | no placement was given, or the frame needed organising and the composition slot was left empty |
| invented lettering, garbled signs and labels | `negativePrompt` | the lettering ban was not added — and check it was not added to a room map, where it destroys the artifact |
| something the shot needs is simply absent | the whole prompt | it is overloaded. **Cut, do not add** — the model drops what it cannot fit, and another clause makes it worse |
| a prop is missing or there are two of it | `Visible / Not visible` | props are named there with their count, and every prop needs its plate |

**Two things this table deliberately does not do.**

- **It does not authorise more rolls.** It sits *inside* the two-failure cap, not
  beside it: it is how you spend the second attempt well. A third roll of a
  changed prompt is still a change of the reference set or the model.
- **It says nothing about where in the prompt a line sits.** The course argues
  both sides — one lesson says move an ignored detail closer to the *beginning*,
  its colour cheat-sheet says the model weights the *end* — and demonstrates
  neither. The thirteen lines stay in the handbook's order.

**One row the course would add, and we do not have.** Its own answer to the
plastic-render row is that the light has no named source, and its fix is a
lighting instruction in the picture prompt. We have no lighting line here by
decision (2026-09-11), so the row above names the levers we actually own
instead. Do not re-raise it unopened
(`research/ai-video-bootcamp/ANALYSIS.md` §4.12).

**Adapted from the Skool course's prompt-diagnosis checklist and mapped onto our
thirteen lines; not measured here** (`research/ai-video-bootcamp/ANALYSIS.md`
§10.4). It changes no field and costs no call.

**One board for every scene's pictures at once** (user's choice, 2026-09-08,
as the cast board): `picsart_asset_review`, split past 12 tiles, one tile per
picture — `tag` the scene and the picture (`sc03 · scene picture`, `sc03 · OTS
over @cal`, `@garage · map`), `prompt` the picture's prompt, `info` the cuts it
serves, one `judge` line: scene picture *"the scene as the card designs it —
these faces, this room, real."*; wide *"the same room, the people small and
far."*; map *"the room from above, four sides labelled, nobody in it."*. Feedback per
`../film/references/WIDGET-FEEDBACK.md`: `approved[]` and `changes[]`, nothing
decided by silence. **A change repairs that picture and triggers an impact check
of its dependents** (`../film-assets/references/ASSET-EFFICIENCY.md`). Rebuild
only images whose required content became invalid: a facial-expression correction
does not remake an unchanged room map.
Geometry or lighting changes can affect many views; inspect rather than assume
reuse. Record the reuse/repair decision and source versions. Approved pictures are saved with the scene
(`film.json.scenes[].pictures`, the URLs the save hands back) and are what the
run carries; the map and the wide are the location's views
(`../film-assets/references/ASSET-SHEETS.md`, *The location*), saved through
`picsart_save_asset` as its row. Two adjacent cuts never open on the same
picture (`references/SEAMS.md`).

**The animatic stays on request** — *"let me see it as stills first"*: the
scene pictures cut in order at each shot's `sec` — a montage bootstrapped
with each picture as a clip (`content.asset.type: "image"`, `duration` = the
shot's `sec`) and opened as `scene` in `picsart_scene_editor` — played at the
film's real length, so the ±10% runtime gate is checked against the reply's
`duration` before anything renders; ask them to watch it once without stopping — playing finds pacing,
scrubbing finds frames. Rendering it to a file is charged and almost never
needed.

**Dialogue durations come from the words, not from a pre-generated line.** A
line's `sec` is its word count at ~2.5 words per second plus the audio line's one
second of silence, snapped to nothing — no line is generated to be measured
(retired 2026-09-04: the model treats a cut time as a share of running time, not
a frame-accurate mark, so the precision was never real). The run's total snaps
to what the model offers; the shots inside it keep their estimates.

**An `END FRAME` stays an editorial tool, on request, per run** — a second
composed image the run arrives on, for a designed transformation or a match cut
into the next run; generated FROM the scene picture (image-to-image), never rolled
fresh, and never on a held look or a line delivered in stillness, where you want
life rather than interpolation.

**Scene-level changes propagate by content.** A weather, light, or time-of-day
change updates the SCENE entity once and recompiles affected headers. Check its
picture dependencies under `ASSET-EFFICIENCY.md`; remake views whose required
illumination/state changed, not every file solely because it shares a scene ID.
Do not retain a reference whose pixels contradict the new state.

## Step 4 — one run before the rest

**With the shot list approved, the sheets locked and the scenes' pictures
approved, buy ONE run before dispatching the rest.** Not a sample of the batch — a test of the path. It answers, for the
price of a single generation, what a whole-film dispatch would answer
expensively and all at once: does the video path work end to end on this
deployment, does the chosen model hold the locked identity across its own cuts,
does it make the cuts it was told to (count and approximate times), does
lip-sync land on a spoken line, and is the recipe (model, resolution, run
length, the reference order, block order, how the cut list is phrased)
worth saving and reusing.

- **On a film that fits one run, the test run IS the film.** There is nothing
  to hold back; buy it with care, judge it on the board, and the "recipe" you
  save is the film's own prompt. On a longer film, pick the riskiest run — the
  one with a *line* in it and the most cuts, since lip-sync and the cut count
  are the two variables with the most ways to be wrong — never a throwaway.
- **Say the arithmetic once, in credits, not in theory**: "the film is one run
  at ~120; a re-roll costs the same again" — or, on a longer film, "one run at
  ~100 instead of the whole film at ~400 — if it holds, the rest go out in one
  batch." Then ask, and wait for the yes — the plan is yours, the purchase is
  theirs. *(Amended 2026-09-07: this line used to end "that is a plan, not a
  permission question; run it", which is how runs went out unquoted.)*
- **Then the conveyor.** A pass unblocks the whole batch — which is quoted and
  confirmed like any other dispatch; a fail has cost one
  run, and what it teaches (wrong model, a cut the model will not make, a
  header it ignores, a descriptor too loose in motion) applies to every run
  before any of them are bought.
- **This is not the "quick test shot" in *Traps*.** That one is a test *before
  the assets lock*, which is the gate itself. This is after the lock — the last
  cheap question before the expensive pass.

Save the winning recipe in `film.json`, per the handbook's own step for
this ("Generation tests: choose model/method per shot; save recipes"), and reuse
it for the batch rather than re-deciding per shot.

## Step 5 — iterations: edit, extend, or re-roll

For a note about the join between clips, route first to
`../film-edit/references/JOIN-REPAIR.md`. Its diagnosis may select a deterministic
edit or a separate connecting insert; the model-routing table below is not a
reason to regenerate accepted footage. Run the prompt-verify step before
dispatching the selected generative repair.

**The note's meaning is the route, and the route is a model call — never a
regeneration by reflex.** A note arrives with a selection — a stretch marked
on the scene editor's timeline (`scene_editor_feedback.timelineComments`, with
its seconds), a pin on a clip (`comments`, with its `layerId`), or a sentence in
chat about a shot — and the two together say
which of three things is being asked. Derive it; do not wait for the word
"edit" or "extend":

| What was selected + what the note asks | It means | The call |
|---|---|---|
| a **stretch** or a clip, and the note is about **how what is there plays** — *"make this more dynamic"*, *"he should look angrier here"*, *"slower"*, *"the mug should be white"*, *"the last 10 seconds are wrong"* | change what exists, inside its seconds | **edit** |
| a clip, and the note asks for **something that has not happened yet** and needs seconds the clip does not have — *"in this scene the copter must land"* when the clip ends with it still in the air, *"let him finish the sentence"*, *"hold on her a beat longer"* | continue the clip | **extend** |
| a clip, and the note asks for the event **instead of** what is there in the same seconds — *"the copter should land here, not hover"* | change what exists | **edit** |
| a note about the **whole run** — light, cast, the cut count — or the words *regenerate / redo / new take* | a different take | **re-roll** |

The test between the first two rows: **does the note ask for something after
the clip's last frame, or in place of what the frames show?** After → extend;
in place → edit. A range selection is almost always an edit (they marked *this*
stretch); a clicked clip with a new event is almost always an extend.

**When the intent is clear, go — no confirmation.** Asking "did you mean edit
or extend?" on a note that already says which is the ceremony this pipeline
keeps removing. **Ask only when the note genuinely reads both ways** — *"make
the ending better"* on a whole clip could be either — and then one short
question naming the two readings in the user's own terms (*"change how the
landing plays, or add the landing after what's there?"*), then the call. Never
two questions, never a menu of models.

The three calls:

| The user says | The call | What goes in |
|---|---|---|
| **edit · change · fix · replace · remove · "the last 10 seconds are wrong"** — anything about what is *in* the clip | **the edit sibling (`shoot.editModel`)** | `videoUrl` = the clip (the run's file, or the piece the shot lives in); `prompt` = the change and *only* the change, naming the shot and its seconds; `imageUrls` = the same numbered views and frames the run had, so identity does not move; `generateAudio` as the run had it |
| **extend · expand · longer · continue · "add a beat at the end"** — anything about *more* clip | **the extend sibling (`shoot.extendModel`)** | `videoUrls` = [the clip]; `prompt` = what happens in the added seconds, written as one more cut header (positions restated, the same references named by number); `duration` per the model's own definition — read `picsart_model_params` and preflight before the first use, and record which it is |
| **regenerate · redo · new take · "roll it again"** — or a note about the whole run: light, cast, the cut count | **a fresh roll of the run** | one header or block changed, the rest byte-identical; it costs the run, said in credits before it goes, and every other shot comes back different — a fresh roll is a fresh roll |

### Extend input gate — probe before preflight (measured 2026-09-12)

The live Seedance 2.5 extend path exposed provider checks that
`picsart_preflight` did not catch. Before dispatching that sibling, probe every
distinct input video and enforce both limits yourself:

- every input video is at least **1.8 seconds** long;
- the **sum of all input-video durations is at most 30.2 seconds**.

These are observations from the current `dreamina-seedance-2-5` r2v worker, not
timeless catalog doctrine: read `picsart_model_params` first and treat a newer
live schema or provider diagnostic as authoritative. Preflight still runs after
this gate for payload validity and price, but a green preflight alone is not
evidence that these two duration checks pass.

For a two-sided seam repair, never send the whole following run merely to show
the target pose. Make a deterministic excerpt of its opening, normally **2.0
seconds**: `picsart_media_apply_scene_template` with `mpscene://montage`, one
clip and the required trim → `picsart_media_validate_scene` →
`picsart_media_export` as mp4 → probe the exported excerpt again. If the source
side plus that excerpt still exceeds the total ceiling, trim the source input to
the shortest motion-bearing tail that the model accepts; keep the original
takes untouched.

The measured extend schema accepts `videoUrls` and no `imageUrls`. Do not attach
reference pictures when the live schema omits them. On hosts whose generic
`picsart_generate` surface exposes only singular `videoUrl`, put the model's
multi-video field in `extra: { videoUrls: [...] }`; the generate tool flattens
`extra` before model validation. Pass `videoUrls` directly to
`picsart_preflight`. `duration` is the requested generated continuation, not
the sum or trim length of the source videos.

Every retry gets a fresh returned job handle. Put that exact handle on the new
render monitor row; never reopen a failed handle or let an old monitor stand in
for the retry.

**Regenerating a clip the user wanted kept is the failure this section exists
for.** A selected clip with the note "expand" was answered with a new prompt
and a fresh roll (reported 2026-09-04): the footage they liked was gone and a
run's price was paid for a beat's worth of new seconds. The note said what to
do; the selection said where; nothing in it asked for a new take.

**What each call must carry so nothing breaks:**

- **The same references, the same numbers.** `imageUrls` for an edit is the
  run's list verbatim — same sheets, same order, same roles in the prompt — so
  the edited clip's faces are anchored by the same pixels the run was. Never a
  fresh selection, never a re-ordering.
- **Verbatim is the floor, not the ceiling: an edit that opens the frame up
  needs the place's pictures ADDED (2026-09-10).** The run's list anchors what
  the clip already showed. The moment the edit changes what is *visible* — a
  pull-back, a wider reframe, a camera move that reveals a skyline, a street, a
  far wall — the newly revealed area is anchored by nothing, and the model
  invents it. So before an edit like that goes out, ask what the new framing
  will show that the old one did not, and put the **map and the wide** of that
  place in `imageUrls` (appended, so the existing numbers do not move) with the
  geography line in the prompt. **The failure this is written from
  (2026-09-10):** clip 3 carried only a tight frame of its setting; the map and
  the wide were not in the run and so were not in the edit either; the edit
  pulled back, and the skyline it revealed was a new place. Rule 10.
- **The change alone, named to the second.** An edit prompt is *"cut 3
  (13.0–19.0s): the mug on the bench is white ceramic, not steel; everything
  else unchanged"* — not a re-description of the shot. A re-described shot is a
  re-rolled shot wearing an edit's price.
- **An extension is a header.** The added seconds are written like any cut
  header of the run — positions restated, the action in beats, the line in
  quotes with its silence, the references by number — because the model reads
  the continuation the same way it read the run.
- **Frames as references are exported at full size** (step 6, *Frames the model
  reads*); a thumbnail is rejected or anchors a soft frame.
- **Preflight, quote, then wait.** Each sibling has its own rate; the number
  goes to the user before the call and the call waits for the yes. A repair is
  a purchase like any other.

**After the call:** a focused join candidate stays separate from the accepted
cut and follows `../film-edit/references/JOIN-REPAIR.md`'s comparison workflow.
Otherwise, the result goes into the cut and the whole cut goes back into
`picsart_scene_editor`, with one line naming the stretch that changed; an edit
replaces the clip's
url in `runs[].pieces` (the shots re-point to it, trims re-derived by the user
only), an extension appends a piece. Split the result at its actual cuts before
anyone judges it — an edit that changed the cut count is a failed take like any
other. Log which call it was, with its prompt and rate.

**Rules that hold across all three:**

- One change per call: one thing edited, one beat extended, one header
  re-rolled. Two changes at once and you will not know which one worked.
- **Log every generation**: run or piece, which call, prompt version, what
  changed, result URL, verdict per cut. `film.json` runs carry `promptVersion`,
  `takes`, `editPasses`, `extends` and `pieces`; the prose log lives in
  `docs/log.md` when there is a disk.
- A clip that will not take an edit in **two** tries, or a run that will not
  land in **four or five** rolls, has a wording problem exactly never. Simplify
  the header that keeps failing: drop an action, change the angle, lengthen
  the beat.

## Step 6 — select and accept takes

**The board opens on the run as delivered — split at its cuts, untrimmed.**
When the batch lands, **split every run's take at its delivered cuts**: look at
the frames either side of each declared time (`picsart_media_contact_sheet`, or
the scene editor's own playhead), place a compositor split where the picture
actually changed, write the result to `runs[].cutTimesActual`, and treat a cut
count that disagrees with the cut list as a failed take before anything else is
judged. The split is bookkeeping over one source file and costs nothing; it is
what makes every shot of the run a clip again. Then open the board on exactly
that: every shot a cell bounded by the model's cut, no trims derived, nothing
shaved. **The automatic rough cut is off (2026-09-04)** — the model already made
the cuts, and a video edge is joined by the handbook's angle change
(`references/SEAMS.md`), so there is nothing to align;
`film-edit` keeps what the pass did and why it is parked. Trims are the user's,
made in the scene editor, and land as `trim` with `trimSource: "user"`. With
several takes competing for a run, you pick the stronger, splice it in and name
the other in a clause (below): the scene is one timeline and cannot hold two
versions of the same shot. Say nothing about cutting
before the board opens — there was no cut; say what came back, and open it.

### A batch that lands opens a board. Every time, without being asked.

**Generations coming back IS the trigger** — not the user asking to see them,
not the takes being good, not the batch being small. First pass, retake, single
shot, a re-roll after a model swap: material arrived, so a board opens in that
same turn. There is no state of the world in which the right response to a
finished generation is a paragraph about it.

This is a reported failure, not a hypothetical. Four takes came back; the reply
read *"1A, 2A, and 3A all check out clean on Seedance — matching their beats, no
issues. Only 4A still needs work"*, then offered a choice between a stronger
prompt and splitting the shot. No board was opened. The user's next message was
*"Why don't you open view review widget?"* — and by then they had been told
three of their four shots were fine without having seen a single frame of any of
them. Three separate rules broke at once:

- **The board never opened**, so nothing was reviewable.
- **The verdict was self-certified in prose.** "Matching their beats, no issues"
  is a taste verdict on material the user has not watched. It is the same class
  as the asset-sheet failure in `film-assets` ("never self-certify identity in
  prose while the user has seen none of the frames"), and it is worse here,
  because a take is what they are paying for. **You may say what you observed;
  you may not say it passed.** "4A drifts the same way on a third model" is an
  observation and belongs in the message. "1A, 2A and 3A check out clean" is an
  approval, and approval is theirs.
- **The turn ended on a menu.** Two craft options were offered as a question
  when the pipeline already answers it: a third take drifting the same way on a
  second model is not a model problem and not a wording problem — step 5's rule
  is that a shot which will not land is *simplified*, split in two or reduced to
  one action. Do that, say you did it, and put it on the board. A question is
  for a decision that is genuinely the user's; "which of these two repairs
  should I try" is your job.

So the shape of that turn should have been: open the board with all four takes,
say in one line that 4A drifts toward calm on a third model and is being split
rather than re-worded, and let them verdict the other three themselves.

Review takes in **one `picsart_scene_editor` that carries the whole film,
by default** (rule 8, `../film/SKILL.md`). Focused join comparisons follow
`../film-edit/references/JOIN-REPAIR.md` instead, without promoting their preview
to the accepted cut. For ordinary review, not the batch that was just
dispatched — the film: every accepted take plus the new one, in playback order,
bootstrapped into one montage scene (`../film-edit/references/ASSEMBLY.md`) and
passed as `scene`. Two accepted videos and a third just delivered is a scene of
three, the new one in its story position. `videoUrl` is for a film that has
exactly one video and nothing else to play beside it.
On a run the scene's clips are the run's cuts, so a comment maps straight back
to the header that produced it — by the pin's `layerId`, or by its time
(`../film/references/REVIEW-FEEDBACK.md`, *Mapping a comment back to a shot*).
The editor carries no prompt or model, so keep that link in `film.json`. The
scene is **one** timeline, and that timeline is the film.
**Never one view per take**: three takes on three boards means three separate sends, and the
first send moves the conversation forward while the other two boards die
unanswered — the documented flunky-confirmation failure. One board, one send,
verdicts for the whole batch. The same rule covers the editor's own edits: when
the user trims or splits in it, that cut is theirs and already seen — record it
from the returned `document` and say so, never an editor per piece
(`film-edit`). If several boards are ever genuinely open at
once, one board's feedback is one item of the queue: act on it, return to the
queue, and advance nothing until the queue is empty or the user says move on.

**Every retake is seen before it becomes the cut — and the surface it goes on is
the scene editor (2026-09-07).** `picsart_video_compare` is out of this
flow: one surface judges picture. What survives from the old compare rule is the
part that mattered — the user sees **every** retake, not just the first, and
nothing becomes the cut behind their back. The failure above ran a shot through
two models and a third generation without the user seeing any of them, which is
how "still needs work" became something only I knew.

- **A retake goes INTO the cut, and the cut goes up.** Swap the new take in,
  rebuild the scene, open the editor on the whole thing, and say in one line
  what changed. Two versions of one shot are never both in the scene: it is the
  timeline, so they would lay out end to end as two different shots of twice the
  real length, with no way to say which wins.
  This ordinary-review rule does not forbid explicitly labelled join comparison
  previews under `JOIN-REPAIR.md`; those are not the accepted movie timeline.
- **With two takes of one shot in hand, you pick — and say so.** Which of two
  candidates is stronger is a craft call, like which repair to try; make it,
  splice it in, and name the other in a clause: it is in the log, one word away.
  Asking the user to choose blind between two files is the old compare board's
  job, and it is gone.
- **An edit or extend return is the same path** — into the cut, the whole cut
  back on the board, one line naming the seconds that changed. That is where an
  edit which fixed the hand and lost the light actually shows.
- **Going back stays free.** Every superseded url stays in the log, so "use the
  first one" is one call and no credits. That is the honest replacement for a
  lockstep A/B: the comparison happens in sequence, inside the cut, and the old
  take is a word away.
- **A re-rolled run** replaces the run's take the same way: the new take split
  at its own cuts, its shots as the scene's clips, and one line on what you
  changed between the takes.
- If a shot is on its third attempt, say the attempts are converging on nothing
  and that the shot needs splitting. The board is where that gets said.

Contract in `../film/references/REVIEW-FEEDBACK.md`.

**Frames the model reads are exported at full size, never thumbnailed
(2026-09-04).** `picsart_media_contact_sheet` renders thumbnails — 480 px wide
by default — for *looking*; fed to the model as a frame, one came back 480×270
and Seedance rejected it (it refuses a picture with **either side** under 300 px
— width counts as much as height — and a small frame that is accepted anchors a
small, soft composition). A frame that will
travel in `imageUrls` is extracted with `picsart_media_export`: a one-layer
scene of the run, `mediaType: "png"`, `startTime` at the wanted second, at the
run's native size (`picsart_media_probe_media` the run first for width, height
and fps). Then **probe the exported frame**: if its width or its height is below the
run's, you have a thumbnail — stop and re-extract; **and neither side may be
under 300 px**, which is the check a 252-px-wide crop got past on 2026-09-09. It is an export (a render wait, a
Drive file, and it counts against the reference byte budget), and it is the
only way a frame from a take becomes a `START FRAME`, `END FRAME` or `FRAME
REFERENCE`.

**Kept frames are anchors, not notes.** The scene editor has no keep-frame
button: a pin whose `instruction` asks to keep what is on screen (*"keep this
face"*, *"this frame is right"*) is a kept frame at its `time` — a second on
the scene's timeline, so map it to the take's own second first
(`../film/references/REVIEW-FEEDBACK.md`). Each one survives pixel-for-pixel:
export that frame
from the take at native size (above — never a re-render, never a thumbnail) and
feed it back. On a run it
goes into the re-roll's `imageUrls` labelled as that cut's frame (*"IMAGE 9 —
FRAME REFERENCE for cut 3: shot 3 as approved, hold this face and this
framing"*), named in the references header and in that cut's header, or as the identity
reference for an edit pass on that stretch; two kept frames in one cut label as
its opening and closing frames. A user who wants a clean rebuild says so, or
removes the pin before sending: if a keep pin is there, honour it. Kept frames
can also ride on an approved cut, where they are simply on the record for
whatever gets regenerated later.

**When the face is right but the shot is wrong, keep the frame.** Do not throw
away an identity the model just got right: save the best frame of that take and
carry it into the re-roll as that cut's labelled `FRAME REFERENCE`, with the
header's motion rewritten and nothing else — the action, the camera, the
progression, and nothing that re-describes what the frame already shows. This
is the handbook's own frame-reuse technique, and it is also how a good frame
from one shot becomes the `FRAME REFERENCE` of a later one.

**When a run changes a place, its frames become the next run's pictures of
that place — automatically (2026-09-08, the handbook's continuity rule).** The
written prompt cannot carry state: a room wrecked in run A, a character covered
in dust, exists only in run A's frames, and the approved scene pictures now
show a clean room that *fights* them. So: the story says where the
story changes a place (the fire, the fight, the flood); every later run at that
place is **dependent** and waits for run A's accepted take, and every other run
dispatches together as now. When run A's take is accepted, pull the frames
without a stop: list every angle the dependent run will see; for each,
`picsart_media_contact_sheet` at the times of run A's last beat on that angle
(up to eight thumbnails, free) and pick the frame that is sharp, mid-action
where the next run continues the movement, and shows the place in its new
state; export each pick at native size (`picsart_media_export`, above) and
probe it. **One frame per angle** — a frame is one camera angle and carries the
state of the scene, not the geography of the room; an angle with no usable
frame means run A holds that angle for a beat (an extend) or the dependent run
is not ready. The exported frames **replace** that place's pictures for the
dependent run — scene picture, and the map when the room's shape
changed (a new map from the widest frame with the handbook's map prompt) —
never attached beside them: two versions of one room is exactly the
contradiction the rule removes. **The same for a person the earlier run
changed**: the frame that shows the state becomes that person's state view and
replaces their wardrobe view in the dependent run (rule 7, 2026-09-10) — a
clean coat beside a torn one is the same contradiction. Record them as `pictures.<state>` on the scene,
name them in the `notes` line when run A's scene opens in the scene editor so
the user sees which were chosen and can swap one, and dispatch the dependent run — its charge was quoted
with run A's in one batch and approved once, so nothing waits on a second yes.

A take is accepted against the checklist, not vibes:

- Characters and location match their references — face, costume, space.
- No artifacts: hands, teeth, extra fingers, drifting objects, random pseudo-text.
- **Every declared cut landed as a cut** — the right count, each near its
  time, a hard cut and not a whip pan, a morph or a dissolve. A run that made
  the wrong cuts is rejected whole, whatever else it got right: its shot
  boundaries are not where the plan says, so nothing downstream lines up.
- Camera movement is what the shotlist ordered. **When the camera is wrong, it
  is a camera-change retake — and an ANCHORED one.** Swap exactly the move
  preset (one block-7 recompile of that shot's header) and never re-prompt the
  whole shot over a camera verdict. On a run this is a re-roll with one header
  changed — a camera move is motion, which an edit pass does not change — and
  the anchoring is the same numbered references, so nothing else in the call
  moves.
  - The order arrives as a comment that asks for a different camera move and
    nothing else. The editor has no preset panel, so read the words; a move
    named in them is the shotlist board's own vocabulary, so it recompiles to
    exactly one block.
  - **Anchor it.** Take a frame from the start of that shot's source (the
    commented clip's source url) and
    send it as a *reference image* beside the shot's locked asset references —
    all of them as references together — then name the opening frame in the
    prompt. Cast, wardrobe, set and light survive as pixels; only the camera
    changes. Not a first-frame role (a frame role and reference images are
    alternatives, and the pair is rejected after the wait), and never the old
    take as a reference video (a reference video supplies camera motion, so it
    reinstates the move being changed).
  - A note that moves the camera **and** something else is an ordinary
    regeneration — an opening-frame anchor would fight a new lighting note.
  - **For a user-requested join repair, distinguish a crop from a new camera
    move.** Existing-pixel reframing follows `film-edit`'s compositor guidance
    after checking the live operation; it is not a generative camera change.
    The old crop-path implementation was removed (2026-08-31). This
    routed a dead-but-usable framing note to a crop-path push-in in the export,
    free instead of a retake; the export half of crop-path was never wired up,
    the code is deleted. Do not infer that all compositor crops are unavailable;
    genuine new camera motion still needs a generative repair.
- The performance fits the task; the line reads; lip-sync holds. **"The line
  reads" has a method** when the classic Picsart API plugin is installed:
  `video-transcribe-audio` the take and diff against the scripted line — it
  catches lip-sync saying the wrong words and verifies the one-second silence
  tail. Without that plugin, judge by ear and say so.
- **Look INSIDE accept candidates before promoting**: a take's first frame
  hides mid-clip morphs. On the takes you intend to accept (only those —
  contact sheets cost render time, not generation credits), render 3–4 frames across the take
  (~25/50/75%), view them in one batched `picsart_view_image` call, and check
  identity and artifacts at each.
- It will cut with its neighbours: a movement written to carry across a cut
  ends mid-move and is picked up; a look that answers a thing leaves frame on
  the right side; the cause is shown before the answer. At a video edge, the
  last shot ends in the middle of its movement and the next video's first shot
  is a clearly different angle (`references/SEAMS.md`). If a join fails, inspect
  whether existing footage can be recut before deciding it needs new material
  (`../film-edit/references/JOIN-REPAIR.md`).
- Palette and light match the world.

Accepted: the run's URL and its `cutTimesActual` into `selects/scNN/` (and
`picsart_drive` — CDN links expire), its prompt saved as the final version,
every shot in it status `final`.

## Step 7 — run it as a conveyor

Work in scene batches, never scattered shots — a scene keeps light and continuity
in one head. A run at a place an earlier run changed is dependent and waits for
that run's frames (step 6); every other run goes out together. While scene N iterates, shotlist N+1 and hand scene N−1 to
`film-edit` for parallel assembly (that is where reshoot orders come from). Track
daily output in shots-final-per-day against the target and say so when the pace
will not make it.

Gate out (per scene block): every run's take accepted and split at its delivered
cuts · all shots `final` in selects · final prompts versioned · log complete ·
the run watched on the board at its cuts and its run edges (the ten-second
version of the edit's join QC, before problems compound).

## Costs

| Free | Charged |
|---|---|
| shotlist, shot design on the cards, run grouping, prompt assembly, preflight, splitting a delivered run at its cuts, splicing a repaired piece back, animatic review on the board when requested, the handoff frames of a changed place (contact-sheet thumbnails to choose, full-size exports to use — render time, not credits), the 1280 px check copies of every picture (`picsart_media_export`), frame exports for anchors (`picsart_media_export` — render time and a Drive file, not credits, 2026-09-07), `picsart_shotlist_board` / `picsart_scene_editor` (renders nothing) | every scene's pictures — scene picture, wide, coverage, a room map when needed (image rates, one board for all scenes), cut-point and take-QC contact-sheet frames (per frame), every draft and re-roll (`picsart_generate`, video rates — billed at the generated length, which is the reason everything upstream of this skill exists), every edit or extend call (each sibling's own rate — preflight first) |

## Traps

- Dispatching on a number the user never saw, or on a yes that covered an
  earlier call. Every charged video call carries its own quote and its own yes;
  the forecast is not a purchase.
- Generating a "quick test shot" for an unlocked scene. That is the gate, and it
  exists because half of early material gets redone.
- Pasting a descriptor into the prompt, whole or shortened. Since 2026-09-09
  the pictures carry identity and the prompt names them by number
  (`references/PROMPT-BLOCKS.md`); a paragraph beside the pictures makes the
  model average words and pixels.
- Dropping the geography line or the map to save tokens — characters swap
  sides between shots and the scene stops cutting.
- Dispatching a prompt that points at a panel of a bigger picture. That is a
  lint error, not a phrasing choice: the reference is wrong, and the crop that
  fixes it is free.
- A run without its scene's pictures, or a place generated empty from its
  descriptor. The handbook's order is scene first, place from it — an empty
  location renders unreal, and a run without the pictures asks the model to
  invent the room (2026-09-08).
- Attaching the approved scene pictures beside frames from a run that changed
  the place. Two versions of one room; the frames replace the pictures.
- Cropping, mapping or using a wide before its visual check has passed.

- A third roll of the same two-shot after two failures on faces. Change the
  reference set or the model.
- Iterating two variables at once. When it improves you will not know why.
- Re-submitting a generation because the call "timed out". The render survived
  the severed call and already charged — poll `picsart_job_status` first,
  always.
- Letting accepted takes sit in the raw pile — the editor must only ever see
  selects.
- Generating a scene shot by shot out of habit. Measured worse (2026-09-03):
  the run is the initial-production unit; a scoped connecting insert between
  accepted clips is the repair exception, not a new production default.
- Treating the run ceiling as a reason to make one long shot. A 30s run should
  usually contain several 3–8s shots; one 30s shot is a long take with its
  reason on the card, not the default meaning of "one run."
- Filling the `startFrame` field on any video — a run, a five-second bridge, a
  one-shot reshoot. It locks out `imageUrls`, and a frame shows only the people
  in it (the 2026-09-08 bridge: one face in the frame, no picture of the other
  woman, a long coat and two flasks). The start frame goes in *with* the
  references, labelled `START FRAME` in the tagging header — every image
  numbered with its role.
- A person in a shot block without their identity and wardrobe views in `imageUrls`, or
  an object named in two places ("on her strap" and "in her hand" is two
  flasks).
- A shot block that opens without its shot type and camera move. The model
  guesses both (the handbook: *"Tell it clearly."*).
- A prompt that refers to the video before it — "continuing from the previous
  scene", "as before", "again". The model never sees that video; write what is
  in the frame now (`references/PROMPT-BLOCKS.md`, first language law).
- A contact-sheet thumbnail used as a reference, a descriptor's picture or a
  start frame. Thumbnails are 480 px previews for choosing; the frame is
  exported at full size first (the 2026-09-08 reshoot failed on two of them).
- Splitting a delivered run at its PLANNED cut times. The model cut near them,
  not on them — split where it actually cut, or every trim downstream is off by
  the drift.
- Regenerating when the note asked for a change or a continuation. "Make this
  more dynamic" on a marked stretch is an edit; "the copter must land" on a
  clip that ends mid-air is an extend — with or without the words *edit* and
  *expand*. A regeneration in their place throws away the clip they wanted to
  keep (reported 2026-09-04).
- Re-describing the whole shot in an edit prompt. An edit names the change and
  the seconds, nothing else; a re-described shot is a re-roll at an edit's
  price.
- Feeding the model a contact-sheet thumbnail as a frame. A frame the model
  reads is exported at the run's native size and probed before it goes.
- Polishing re-roll 8 of a run whose failing header should have been simplified
  at re-roll 4.

Referenced files: 6

motion64.7 KB

View saved version →

---
name: motion
description: Turn static material into a real motion-designed video — a Figma design's LAYERS, or the user's own uploaded assets (photos, logos, screenshots, clips), become a composed, animated ad/reel where elements slide, scroll, type on, parallax, and smart-animate, tuned by eye, then rendered. The user can describe the motion they want, or let the skill analyze the assets and propose a concept. Use when the user wants to animate a design or assets, "make an ad/reel/video from these images/frames/screens", add motion to a Figma flow, or bring static material to life. Not for stitching footage the user already edited, and not for generating footage from a pure text idea (that's text-to-video).
---

# ChatGPT and Codex integration

Apply these host conventions throughout this skill and its references.

- Discover the connected Picsart tools and use their actual names and schemas.
  The `picsart_*` names in the workflow identify tools, not a fixed host namespace.
  If the connection is missing, explain that Picsart must be connected; continue
  script and shot planning when possible. Never invent tool results or renders.
- Read board decisions from the current user message and attached widget context.
  Use `read_widget_context` only if the host actually exposes that tool. Do not
  assume ChatGPT has Claude's context reader. Treat a board's initial tool result
  and unsent widget state as previews, never as user approval. If a submitted
  decision has no accessible payload, explain briefly and use the text fallback
  to collect the missing decision; do not guess or repeat charged operations.
- Use the host's available question interface or one concise conversational
  question for preset choices.
- UI rendering and delivery of feedback are the remote MCP server's responsibility.
  If a board is unavailable, present its relevant options in text and record the
  user's selection using the same documented feedback fields. Do not claim that
  a plugin manifest makes Claude-specific widgets compatible with ChatGPT.
- Save `motion.json` and artifacts in the user's writable project workspace, not
  the installed plugin directory. If persistent file tools are unavailable,
  keep a structured project checkpoint in the conversation and provide it for
  download when the host supports files. Never claim a file was saved without
  confirmation from a file tool.
- Honor the user's current instructions and already authorized scope. Preserve
  quoted spend limits and production locks; do not infer authorization from a
  preview, a timeout, or a connection being installed. A timed-out charged call
  needs a status check before considering a retry.
- This package holds six film skills (`film`, `film-development`, `film-assets`, `film-scenes`, `film-edit`, `film-finishing`) and three motion skills (`motion`, `motion-import`, `motion-design`). Route between them directly.
- `motion-import` reads a design's layers through the Figma app's tools. If Figma is not connected in this host, use its flat-frame fallback and say so once.


### Motion server compatibility

These host conventions qualify tool and widget instructions below. Discover
live tool names and schemas before invoking the pipeline. A ZIP update changes
instructions; it does not deploy new server tools.

When `picsart_scene_editor` is available, it accepts `scene`, `title` and
`notes` and emits `scene_editor_feedback` v1. Accept without edits or comments
returns `verdict: "approved"`; Submit after edits or comments returns
`verdict: "needs_changes"`. Read the returned `document` when `edited` is true:
frame comments carry `instruction`, and timeline comments carry `text`, `start`
and `end`. Opening the editor is a preview, never approval. If the tool is
absent, use the preview and text-review fallback already described in this skill.

Dedicated import/gradient/tracking tools in the new workflow may require a
newer MCP deployment. Call them only when exposed. Use a documented live
recipe or the source's explicit fallback when it supports the same operation;
otherwise explain the missing capability before proceeding with that step.
Keep the original editable assets and the current scene intact.

# Motion — a static design becomes a motion-designed ad

The frames are not the video. They are a **design**, and a design is made of **layers**:
panels, headlines, buttons, images, lists. Motion design animates those *elements* — a
list scrolls, a headline types on, a card slides in with a stagger, a tap-dot pulses,
the shared chrome **smart-animates** from one screen to the next, everything moving with
depth. Bind a frame as one flat PNG and all of that is impossible — you get a slideshow.

So the first principle of this pipeline: **the design comes in as layers, and the video
is a composite of moving elements — never a montage of flat screens.** A real ad is
built the way the After-Effects reference is built: each element on its own layer, each
with its own motion, shared elements carried between frames.

One economic law runs underneath: **the motion loop is free; the one paid capability is video
generation, and it is always credit-gated.** Every tool the compose/motion/preview loop touches —
layer ingest, compose/`patch_scene`, layout queries, `validate`, the panel, and the renders
themselves (`picsart_media_export`, `picsart_media_contact_sheet`) — is on the connector's
zero-credit list (verified against live billing), so for all of that: **never tell the designer an
action will cost credits, and never hesitate to render for cost reasons — there is no cost.** The
**sole exception** is **generating video** (`picsart_generate` and the generative tools), which spends
credits — and it **never runs without the designer's explicit, informed consent**: show the balance,
quote the exact cost, get a yes (full gate in *Video generation* below). No other part of this
pipeline charges.

**Ignore the "charged"-looking badge on `export` / `contact_sheet`.** Both dispatch a real GPU
render and leave a Drive-saved file, so they carry a *behavioral* annotation (`readOnlyHint:
false`, artifact-producing) that a connector UI or permission prompt may surface as "writes" /
"charged." That flag is about **behavior, not price** — the credit cost is still **zero**
(they're in the server's `FREE_TOOLS`). Do not read that badge as a credit charge or warn the
designer about it. Still author with care: a render costs *time* even when it costs nothing, so
decide on the panel's free previews and render the export when the reel is worth rendering.

## When to read which reference (mandatory, lazy)

References are not loaded with this skill — **read each one at its step, before doing that
step**, not "if you happen to need it":

| Before you… | Read |
|---|---|
| touch any tool, or wonder which tool answers a question | [`references/TOOL-ROUTING.md`](references/TOOL-ROUTING.md) |
| compose ANY scene — the rules **both** paths share (highest-level-tool, hosting/CSP, Drive, live text/fonts, placement, treatment, the eye) | [`references/COMPOSE-FUNDAMENTALS.md`](references/COMPOSE-FUNDAMENTALS.md) — read alongside whichever compose path applies; **the tools are the same, only the ingest differs** |
| import a **Figma** design / compose frames from its layers | [`references/AUTHORING-FLOW.md`](references/AUTHORING-FLOW.md) — the Figma golden path (coords, layer kinds) on top of the fundamentals |
| compose a scene from **uploaded assets** (photos/logos/screenshots/clips) — grid / stack / collage / montage | [`references/ASSET-COMPOSITION.md`](references/ASSET-COMPOSITION.md) — the non-Figma compose path (upload, probe, layout, fit-to-canvas) |
| decide *what* motion a change deserves (easing, duration, reduced-motion) | [`references/MOTION-PRINCIPLES.md`](references/MOTION-PRINCIPLES.md) |
| author a named pattern (scroll, type-on, tap-dot, progressive text) | [`references/MOTION-PATTERNS.md`](references/MOTION-PATTERNS.md) |
| author a seam between two frames | [`references/TRANSITIONS.md`](references/TRANSITIONS.md) |
| stagger/sequence multiple elements in one beat | [`references/CHOREOGRAPHY.md`](references/CHOREOGRAPHY.md) |
| choose ANY motion, or author scene JSON — the full menu of **layer kinds, motion presets, text animations, looks, effects, transitions, masks** (Figma AND from-scratch alike) | [`references/MP-SCENE-VOCABULARY.md`](references/MP-SCENE-VOCABULARY.md) — enumerate it via `get_capabilities`; don't hand-roll what a preset/look/effect already does |
| act on any widget/panel response | [`references/WIDGET-FEEDBACK.md`](references/WIDGET-FEEDBACK.md) |
| validate a scene — before showing ANY render, or when "it validates but looks wrong" | [`references/SCENE-VALIDATION.md`](references/SCENE-VALIDATION.md) — schema ≠ correct; the eye is the real check, ground truth branches by route |
| a render/preview looks wrong or a tool errors | [`references/MOTION-TROUBLESHOOTING.md`](references/MOTION-TROUBLESHOOTING.md) |
| write the first keyframe / nested comp / zoom math / opacity from Figma | [`references/ENGINE-GOTCHAS.md`](references/ENGINE-GOTCHAS.md) — silent-bug rules (local clocks, one track per channel, sRGB→linear opacity, fixed-point zoom) |

## Context discipline — never load the whole design at once

Tool results live in the context window for the rest of the session, so what you fetch is what you
pay for — in tokens, in cost, and in the model's attention. **On Claude Desktop / the MCP connector
(where this plugin runs) there is no file-offload: a big result goes straight into context or the
call errors** — a whole-page `get_metadata` on a real storyboard is ~100KB+ and overflows. So fetch
narrowly, hold little, and persist facts to state:

- **Frame INDEX once, cheaply.** Read the top-level frame list — ids, names, left→right order,
  dimensions — from the page/canvas node **without drilling into each frame's tree**. That's all you
  need to know what exists and the running order.
- **Full layer tree per PAIR, then release it.** Pull a frame's full nested tree only when you reach
  its pair, and keep at most the **current pair (A + B)** in working context — don't accumulate frame
  7's tree while authoring 2→3. **The payoff of that narrow breadth is affordable DEPTH:** because
  you're holding only one or two frames, you can retrieve *everything* about them — every sub-layer,
  exact coords, ink bounds via `use_figma`, styles — which is exactly the completeness correct
  composition needs and which you could never afford across the whole storyboard at once. Narrow
  breadth buys full depth where it matters.
- **Scan the sequence with COMPACT tools, not full metadata.** Scroll-run / matched-element / zoom
  detection rides on `picsart_media_diff_layouts` / `query_layout`, which return small geometry
  deltas — peek ahead only as far as a run actually extends, never full-read the whole storyboard.
- **Persist resolved facts to `motion.json` / the scene doc, not the conversation.** Once a frame's
  coords, matchKeys, or the frame index are resolved, they live in state — so the next pair doesn't
  re-hold them in context, and a compacted or fresh session re-reads the file instead of re-fetching
  Figma. State is the memory; context is scratch.

## Flat vs. layered — the difference that is the whole product

| Flat-frame montage (the anti-goal) | Layered motion design (this skill) |
|---|---|
| each frame is one sealed PNG | each frame is its **layers**, each movable |
| motion = a transition at the seam | motion = every **element** animates (slide, scroll, type-on, parallax, tap) |
| screens **cross-dissolve** | shared elements **smart-animate** A→B (matched motion) |
| reads as a slideshow | reads as an ad |

Flat montage is the **fallback only** — used when a design's layers genuinely cannot be
had (a rasterized image, no Figma access). When you fall back to it, say so plainly:
*"without the design's layers this becomes a slideshow, not motion design."* Never
choose it while layers are available.

## Scope — phased

- **Phase 1 (now): motion-design static material into an ad/reel.** A Figma design's layers — or
  the user's own uploaded assets — → a composed, animated MP Scene → tuned by eye → rendered. All
  of what follows.
- **Also available: generate footage when the design needs it.** When a beat calls for real
  generated video (a video card/element, or the designer asks to generate a clip with a model of
  their choice), that's supported — but it spends credits, so it runs behind the cost-consent gate
  (see *Video generation*). It's a capability within the flow, not a separate phase.
- **Phase 2 (later): generate a *new* video from a given one.** A video-to-video pass,
  once the server's v2v path is wired. Don't build it yet; keep the hook
  (`motion.json.source` can name a given video, not only a design).

## 0. Triage — route before anything

The plugin animates **static material**, and material arrives several ways. Route first — the route
sets the **ground truth** you'll later validate the rendered result against, by eye:

| User brings | Route | Ground truth |
|---|---|---|
| A **Figma design / flow** with layers | **this skill**, Figma ingest (Stage 1) — run the opening tool sequence in [`references/TOOL-ROUTING.md`](references/TOOL-ROUTING.md) | **match the design** |
| **Their own assets** (photos, logos, screenshots, clips) **+ a described motion** | **this skill**, asset ingest — `picsart_media_upload` the files, `probe_media` them, then compose and author the motion **to the brief** | **match the brief** |
| **Their own assets, but NO motion described** | **this skill**, **propose mode** — analyze the assets (`probe_media` + **view them**), then **offer 2–3 concrete motion concepts** for the user to pick; author the chosen one | **the concept they picked** |
| An existing **MP Scene** to keep editing | **this skill**, resume — open the editor on it and edit | the current scene |
| **Flat exported images of a design** whose layers can't be had | **this skill**, flat-fallback — warn it will be a slideshow, and offer layered if they can share the Figma file | the flat frames |
| Footage already edited, to stitch | not this — that is a plain montage | — |
| A clip from a **pure text idea** (no assets) | not this — that is text-to-video generation | — |

The dividing line is **static material in, motion authored** — the material may be a Figma design
*or* the user's own assets. Note the difference between a **photo/asset reel** (the images *are* the
content — a first-class raster route, not a degradation) and **flat PNGs of a design** (layers were
lost — the announced slideshow fallback); don't treat uploaded photos as a failed layered import.

**Two postures — do not blur them.** With a Figma design or a stated brief you are in a **fidelity**
posture: build exactly that, and *propose* anything extra separately — **never invent** (the whole
"change only what was asked" discipline). With assets and no brief you are in a **proposal** posture:
inventing a concept is the *job* — but you still **offer 2–3 options and get a pick before authoring**,
never run off and build one unasked. Both postures meet at one rule: **a concrete plan is agreed
before motion is authored** — a matching plan in fidelity mode, a chosen concept in proposal mode.

**If the page holds SEVERAL distinct sequences/storyboards, list them and ask which to animate
first.** A Figma page routinely carries multiple reels side by side; don't silently start on the first
cluster. Group the frames into their sequences (by spacing/section/naming) from the cheap frame index,
name what you found, and let the designer pick the running order before composing anything
(`references/AUTHORING-FLOW.md` §1).

**Never start from — or hand back — an animated video pulled from Drive.** `picsart_media_export`
auto-saves its render to Drive, but that video is an **output, not an input**: do not list Drive
for a prior render and reuse it as the result or the source. **Always author the motion from
scratch**, from the design's layers/frames. The only valid inputs are the Figma design (or flat
frames) and an in-progress MP Scene — never a rendered video. A Drive export is read-only
history; open the MP Scene and re-author, don't re-serve the old video.
**The one exception: the designer explicitly asks for it.** If they name a previously created
Drive video and ask to use it ("take the video we rendered yesterday", "use the export from
Drive"), then using it is honoring their instruction, not a shortcut — fetch that video and do
what they asked with it. The rule bans *you* reaching for a prior render on your own initiative;
it never overrides an explicit user request.

## Propose mode — assets in, no brief

When the user brings **assets but doesn't say what motion they want**, you propose it. This is the one
place where inventing is the job — but it stays disciplined: analyze, offer a small menu, build the pick.

1. **Analyze the assets — by looking.** `picsart_media_upload` the files, `picsart_media_probe_media`
   for dims / fps / duration, and **view each asset with your own vision** (the same eye that validates
   output — [`references/SCENE-VALIDATION.md`](references/SCENE-VALIDATION.md)). You cannot propose
   sensible motion for material you have not seen.
2. **Classify what you have.** Photos? a logo / brand mark? app screenshots? a product shot? a video
   clip? text? The class drives the concept: photos → a promo/reel (recipe `photo-promo`); screens → a
   UI walkthrough (`ui-motion`); a logo → a brand sting; mixed → a title + montage.
3. **Offer 2–3 concrete concepts — not one, and never a blank "how do you want it?"** Each concept is a
   short, specific pitch the user can choose between, differing in *approach* (e.g. calm/premium vs
   punchy/ad vs playful), and each naming: the **format** (ratio/fps), the **layout** (how the assets
   are arranged — a grid, a stack, a collage, a montage/sequence, or a single focus; assets need not be
   one-per-screen), the **key moves** (its 2–3 signature animations), and the **rough pacing/length**.
   Use `motion-intent` to derive the moves from the intent each concept expresses, and `apply_scene_template`
   (montage/collage/title families) as a starting layout where it fits. Keep the concepts genuinely
   distinct — a real creative-direction menu, not three shades of one idea.
4. **User picks (or steers).** They choose one, or blend ("option 2 but calmer") — fold that into a
   single agreed concept. That concept is now the **ground truth** you validate the render against
   (`SCENE-VALIDATION.md` rung 4, chosen-concept branch).
5. **Author the pick** — compose the assets into a scene (a scene template or `patch_scene`), author
   the concept's moves (`apply_motion_preset` / `apply_text_animation` / keyframes), then validate by
   eye and preview for approval, exactly as the fidelity path does.

**Deciding the layout — like a designer, not a checklist.** Don't tally asset properties to pick
"show all at once vs give each focus." Decide it the way designers actually do:

- **Message and hierarchy first.** Ask what the piece must *say* — "we have range/lots" makes the
  **set** the hero (grid / collage / stack); "this one is the star" makes a **single** asset the hero
  (focus). Then rank primary → secondary → rest; it's rarely all-equal (even a grid usually has one
  bigger or first tile).
- **Motion usually dissolves the choice.** You can show many **and** guide attention one at a time by
  **revealing in sequence** (a staggered grid/collage), or by **establishing then pushing in** (the
  whole set lands, then the camera moves to the hero). So "together vs focus" is most often a *pacing*
  choice on the same layout, not a hard fork.
- **Budget by time.** How many beats fit the length — nine items in 6s → a grid that reveals; five in
  20s → walk through them.
- **Then let the pick decide.** Spread the 2–3 concepts across that real axis (e.g. all-at-once grid
  vs spotlight-each vs establish→detail) and let the user choose — the decision is settled by reacting
  to concrete options, exactly as a designer roughs out directions rather than deciding by rule.

**Read the theme / occasion, and let it drive the concept.** When the assets or brief carry a theme
or occasion — Christmas, Valentine's, back-to-school, a sale, a brand's own palette/vibe — reflect it:
a themed **colour palette**, and where it fits, a few **themed accent elements** (an authored
snowflake/ornament/heart shape, a seasonal texture) and a matching motion register. Read the theme by
**looking** at the assets (the eye) and from what the user said — don't assume one. Put it in the
2–3 concepts and let the user pick, and keep it to a **few coherent accents** (MOTION-PRINCIPLES §7),
never a snow-globe of decorations.

> **This themed-invention licence is PROPOSAL MODE ONLY.** With a Figma design or a stated brief you
> are in fidelity posture: the theme's colours and shapes are **already the designer's** — respect the
> design's palette and **do not invent themed decorations onto it** (that is exactly the trust-breaking
> invention the "animate existing layers only" rule forbids — see `ENGINE-GOTCHAS.md`). Offer a themed
> addition as a separate proposal; never sprinkle it in.

**Do not skip the menu and build one concept unasked** — that is the proposal-mode form of running
ahead. Propose → pick → author, every time. Once a concept is picked you are back in **fidelity
posture to that concept**: build what was chosen, and propose any extras separately.

## Working mode for uploaded assets — part by part, or the whole motion at once

The pair-by-pair rhythm (Stage 3 below) is for **frame sequences** — a Figma storyboard gives natural
A→B pairs to work through. **Uploaded assets have no frames**, so there is nothing to pair. Before
authoring, **offer the user how they want to work**:

- **Part by part** — build and approve the motion in pieces, beat by beat. A "part" is a **beat, not
  one asset**: a single beat can move several assets together — a grid of photos revealing, a stack
  fanning out, a row sliding in, a title typing over a collage. Safer for a longer or multi-beat piece
  — the user steers as it grows, and one wrong call doesn't compound.
- **The whole motion-added version at once** — author the entire motion, render it, and show the full
  result, then refine from there. Faster for a short or simple piece (a single logo sting, a 3-photo
  reel), where seeing the whole thing is the quickest way to react.

Offer both, recommend by size (part-by-part for multi-beat/longer, whole for short/simple), and let
the user pick — don't assume. Either way the **eye-check + preview-approve gate still applies**
([`references/SCENE-VALIDATION.md`](references/SCENE-VALIDATION.md)): part-by-part approves each piece
as it's added; whole-at-once approves the full render, then iterates.

## Tool names — read this first

Two families on this plugin's `picsart-gen-ai` connector, and they are wired differently:

- **Scene tools — `picsart_media_*`.** Import, compose, validate, patch, translate,
  export, contact-sheet: the media/scene tools, surfaced under the `picsart_media_` prefix.
  Server recipes and older docs name `mp_export`, `mp_patch_scene`, etc.; translate `mp_<x>` →
  `picsart_media_<x>`.
- **Panel tools — `picsart_<widget>`, gen-ai-native and new.** The widget-opening tools
  are **not** media-tools proxies — they are registered directly in the gen-ai server,
  each bound to a `ui://` widget resource. This plugin's is **`picsart_scene_editor`** (the
  scene editor — the pair-review surface: it **plays** the MP Scene document in the browser, free,
  and returns the designer's verdict + edit-request comments; **pass it a `scene` document, never
  video/media**). It is a *review* surface — motion is authored by you through
  `picsart_media_patch_scene`, not picked per-layer inside the panel. `picsart_scene_editor`
  **replaces the old `picsart_scene_inspector`** (and its `mp_inspect_scene` / `mp_suggest_motion`
  drafts) — do **not** read those as tool names; they no longer exist. (This workflow does not require the
  `picsart_motion_setup` format console, which production may expose — the video format
  (ratio/fps/energy) is decided directly; see `motion-import` *Set the format*.) `picsart_scene_editor` is the complete panel
  surface. Full contract: `docs/widget-specs/mp-scene-editor.md`.

**Show the motion in the scene editor — don't re-export a video each time.** After authoring
a pair, open `picsart_scene_editor` **on the whole scene built so far** (title it for the current
pair, e.g. "f3→f4", so the designer knows where to look), so they watch the pair play **in context**
and can scrub straight to it — never a 2-frame sub-scene. That panel is the viewing surface, free,
and cheaper/faster than exporting
just to see a result. It **renders no file** — do not look for a `videoUrl` or save anything to
Drive from its reply. Reserve `picsart_media_export` for the final deliverable and for the error
fallback below. Do **not**
batch — never render a contact sheet of every frame as the main flow (that is what produces "all
frames at once").

**If a `picsart_scene_editor` call errors — a 503, a transport failure, or any
exception — do not leave the designer with nothing.** Export the **whole scene** (the full
reel end to end, **not** just the current pair's A→B window) with `picsart_media_export` and
**show the result video** so they can still see the motion play, then continue the
pair-by-pair flow once the panel recovers. Exporting a preview render is free
(`picsart_media_export` is 0-credit), so this costs nothing. The panel itself already falls
back to a video on a render error; this is the same move at the tool level. The
contact-sheet + the host question interface path is the **last resort** — reach for it only if the
export also fails, and never stall because of a panel error.

**What you SHOW is always the whole reel — even while authoring one pair.** Pair scope is an
*authoring* focus (it makes it easy to work one seam at a time), but every time the designer sees
the motion — the editor preview of a pair, an error-fallback video, any "let me see it" export —
show the **whole scene built so far**, positioned at the current pair, **never just that pair's
window** (e.g. not only the 3→4 transition in isolation). The editor plays a timeline, so opening it
on the whole scene lets the designer watch 3→4 *in context* and scrub to it; a 2-frame sub-scene
throws that context away. "Whole scene so far" = every composed frame up to now (later, uncomposed
frames simply aren't there yet). The designer navigates pair by pair; what they watch is the reel.

**Ground your facts in tools; own your judgment; author through tools.** The real work of
this plugin lives in the tools — but the *judgment* about what to do is yours. Three different
activities, three different rules:

- **Facts come from tools — never invent them.** The design's layers, geometry, stacking
  order, fonts, layout; what changed between two frames; the valid presets/easings; whether a
  scene validates or rendered — these are *ground truth*. **Do not eyeball, describe, or infer
  the composition from the image and act on that guess** — your visual read is not a substitute
  and will be wrong about coordinates, layer boundaries, and fonts. Read each through the tool
  that owns it: `picsart_media_query_layout`, `picsart_media_diff_layouts` (what changed between
  two frames — opacity-blind, so check fades yourself), `picsart_media_get_capabilities`,
  `picsart_media_probe_media`, `picsart_media_list_fonts`, `picsart_media_validate_scene`.
- **Judgment is yours — apply it on the facts, with the designer.** What moves, which preset,
  what timing, which element is focal, essential vs decorative, when to override a default — this
  is the work you are here to do; no tool decides it for you. Drive it from the tool-resolved
  layers + `get_capabilities` presets and judge it on rendered previews (the scene editor, or
  an exported video). Deciding *what* is your job — **but never
  stop at describing it in prose**: author it and show the render.
- **Author through tools — never hand-write the artifact.** `picsart_media_patch_scene`,
  `picsart_media_apply_motion_preset`, `picsart_media_apply_text_animation`,
  `picsart_media_apply_scene_template`, `picsart_media_apply_effect`. **Never hand-write scene
  JSON, or type a coordinate/value you didn't get from a tool** — mint every edit through the tool.
- **Show / preview**: `picsart_scene_editor` (view the motion moving) or `picsart_media_export` (a rendered video). Never describe motion in prose.
- **Validate / render**: `picsart_media_validate_scene`, `picsart_media_export`. Run `picsart_media_layout_lint` too whenever it's in your tool list — a cheap automatic overlap/z-order pass over the whole timeline, done **before** the `contact_sheet` eye check (it's opacity-blind, so the eye check follows it, never replaces it).

The failure to avoid isn't *thinking* — it's **fabricating a fact you could have looked up, or
hand-building an artifact a tool should mint.** Think all you want about *what* the motion should
be; get every *fact* it rests on from a tool, and *author* it through one.

**Without the panel (the `picsart_scene_editor` widget not connected — a valid,
lower-fidelity setup) you STILL must use the media tools**: read via
`picsart_media_query_layout`, author via `picsart_media_patch_scene` /
`picsart_media_apply_motion_preset`, show via `picsart_media_export` /
`picsart_media_contact_sheet`. The panels' absence is a degraded *surface* — it is **not**
permission to invent facts (coordinates, layers, fonts) or hand-author the motion.

If a tool you genuinely need is unavailable (not connected, or it errors), **say so and stop**
— never silently fall back to doing the work yourself and proceeding as if you had the real data.

**Do the least work yourself — reach for the highest-level tool that does the job.** You are an 
orchestrator; the mp-scene server is the engine. Before assembling anything by hand out of
many low-level ops, check whether the server already does it in one call: a **scene template**
(`apply_scene_template` — montage/collage/title families) before hand-building a composition; a
**motion preset** (`apply_motion_preset`) or **text animation** (`apply_text_animation`) before
hand-keyframing; `quickstart` / the **recipes** before inventing a step sequence; `patch_scene`
for *edits*, not for constructing whole scenes op by op. The chat's judgment goes into WHAT to
do (which layer, which motion, what timing — with the designer); the server does the HOW.

## The edit loop — every designer instruction is a tool round-trip

The designer's job is to **point and say**; yours is to **run the tools**. Every instruction —
*"speed this up", "remove the animation from that", "update the easing", "this button has an
extra outline", "make the CTA pop later"* — is handled the same way, and **never** by describing
the change or acting on what the image looks like:

1. **Locate** — resolve the target through `picsart_media_query_layout` (`intrinsics:true`): the
   exact layer id, its box, its active window, its authored content. If the designer clicked a
   layer in the panel, the selection already carries the id + time — still confirm the *current*
   state through the tool, don't assume it.
2. **Edit** — apply the change with the right tool (full map in
   [`references/TOOL-ROUTING.md`](references/TOOL-ROUTING.md)): almost always
   `picsart_media_patch_scene` (`set` to change a value, `remove` to drop one), id-anchored;
   `apply_motion_preset` / `apply_text_animation` when **adding** motion. Values (easings, presets)
   come from `get_capabilities`, paths from `get_scene_schema` — **never invented**.
3. **Validate** — `patch_scene` re-validates inside itself; a `severity:error` fails the batch with
   diagnostics. Read them, fix, retry — never proceed on an invalid scene.
4. **Show** — re-open the panel on the pair (or `export` the whole reel) so the designer **sees**
   the result, not a description. The edit minted a **new `sceneRef`** — always work from the latest.
5. **Repeat** — one instruction at a time, pair by pair, advancing only on the designer's approval.

The whole product is this loop: the designer speaks small, specific edits; you turn each into a
tool call and show the result. If you ever catch yourself reasoning about coordinates, easings, or
layers **from the picture** instead of the tools, stop — that is the failure mode.

## Lean on the server's recipes — don't duplicate them

The scene/media tools reach this plugin **through the MCP connector**: they are the
`picsart_media_*` tools, and their authoritative step sequences are the server's **recipes**. Ask the
server rather than hardcoding:

- `picsart_media_quickstart` — the on-ramp for common jobs; it also surfaces the relevant
  recipe steps.
- `picsart_media_list_recipes` / `picsart_media_get_recipe` — the recipe catalogue and a named
  recipe's steps. **When present, fetch the official recipes before hand-authoring:**
  **`mp-figma-storyboard-to-scene`** (the canonical Figma→scene flow), **`mp-motion-intent`** /
  **`mp-motion-patterns`** / **`mp-motion-mechanics`** (craft), **`mp-transition-authoring`**,
  **`mp-scene-format-architecture`** (the format). **Availability varies by connector** — these
  are commonly **not mirrored today**; if the two tools aren't in your tool list, that's normal,
  not an error: fall back to this plugin's references + `picsart_media_quickstart` and proceed.

**This skill orchestrates; the recipe supplies the steps.** When a recipe and this skill
disagree on a *step*, the recipe wins; the gates, the motion vocabulary, and the panel
turns this skill adds are its own job.

## The pipeline — 5 stages, gates between them

| Stage | Does | Skill | Gate to pass |
|---|---|---|---|
| 1 · Import | Figma design → each frame's **layer tree** (elements + transforms + assets); matched elements tagged; format set | `motion-import` | every animatable layer has an asset/content + transform; matched elements tagged; format set |
| 2 · Compose | rebuild each frame as a **layered MP Scene** (elements placed by transform), verified to match the design | this skill | each composed frame `validate`s clean **and** renders identical to its design |
| 3 · Motion (interactive) | author real motion **one pair at a time** — per-element animation + matched-element smart-animate for frames A→B | `motion-design` | **every pair** approved on a moving preview, not a still |
| 4 · Preview | filmstrip across each pair and the whole cut | this skill | motion reviewed on real frames before the deliverable is rendered |
| 5 · Deliver | from the MP Scene: `export → mp4` for the final video (free); the MP Scene itself stays the editable source | this skill | mp4 exported and verified once |

**Gates are the product.** The expensive mistake is exporting motion the designer never
watched — a still hides everything. A gate passes only on evidence (a tool result, a
recorded waiver, a user's yes) or a conscious override. Before naming the next stage,
print the gate check with ✓/✗ and the evidence.

**Export and Drive-save are user-initiated — never automatic.** Do **not** call
`picsart_media_export` on your own to "show" a result (the editor already plays it on
free frames), and do **not** upload or save the output to Picsart Drive (`picsart_drive`,
`driveSave`) as an automatic step. Both the final render *and* saving to Drive
happen only when the designer explicitly asks for them (or clicks the panel's own
control) — offer them, don't perform them. Producing a deliverable and stashing it in the
user's Drive without being asked is exactly the silent, unwanted side effect this gate
exists to prevent.

## Stage 2 — compose the layers into live frames

> **HARD GATE — READ BEFORE COMPOSING. A frame is rebuilt from its LAYERS, never from a screenshot
> of the frame.** The single most common failure is taking one `get_screenshot` of the whole frame
> (or of a group) and dropping it in as one image. **That is wrong, always.** A `get_screenshot` of a
> frame or a multi-element group is **never** a scene layer. Each design node becomes its **own**
> scene layer, authored as its native kind (text→`text`, rects/cards/borders/circles/masks→`shape`,
> icons/logos→vector, only true photos/video→a raster asset). An icon/logo's SVG paths become a
> live shape via `picsart_media_import_svg_path`, not an uploaded SVG. A node's Figma **paint** (fill,
> box-shadow, gradient) comes over natively via `picsart_media_import_figma_paint`, not by
> baking the node. Only what it reports in `skipped` may stay baked, and you disclose it.
>
> **Definition of done (check it):** a composed frame's layer count roughly matches the design
> frame's node count. A frame that has one `media` layer covering the whole thing, or a handful of
> big raster slabs, is a **failed** composition — throw it out and rebuild from the per-node tree.
> If you don't have the per-node kinds/coords yet, that's the signal to **fetch this frame's full
> layer tree** (you're only holding one or two frames, so you can afford all of it — see *Context
> discipline*), **not** to fall back to a screenshot.
>
> If you catch yourself reaching for a whole-frame or whole-group screenshot "to save time" or "to
> fix CSP," stop — that is the exact shortcut that produces a slideshow instead of motion design.

Do **not** bind frames to `mpscene://montage` — that treats a frame as one flat clip and
throws away every element. Instead **rebuild each frame as a composition of scene
layers**: one scene layer per imported design layer, positioned by its `transform`
(x, y, w, h, rotation, opacity, z-order). Author this with `picsart_media_patch_scene`
(or a compose recipe if the server exposes one), never the montage template.

**Layered means LIVE, not rasterized — a hard requirement.** Text is always a live `text`
layer (font by file URL), never a baked PNG; solid/gradient rectangles, cards, borders, circles
and masks are `color`/shape layers; icons and logos stay **vectors** where supported; a raster
asset is reserved for **true imagery** (photos, videos, textures, or an effect the engine can't
reproduce — disclosed to the designer when baked in). Never flatten a group whose children carry
text or animate independently into one screenshot: a flattened slab can only fade/scale as a whole,
which forfeits every per-element move Stage 3 exists for. The full kind-triage lives in
`references/AUTHORING-FLOW.md` §5.

- **"Upload every asset" means EXTERNAL MEDIA ONLY — it does NOT mean "turn every visual element
  into an uploaded image."** Drive/`upload` applies only to external media files (photos, videos,
  textures, unavoidable raster). **Native scene shapes, vectors, and live text must remain authored
  elements and must never be converted to raster images merely for upload or CSP compatibility.**
  PNG export is a **fallback** for genuine raster content or a truly unsupported vector — not the
  default, and never a way to make a card/component/screen "uploadable." (This is the exact trap that
  produces a rebuilt-from-screenshots frame that diverges from the design.)
- **A CSP/blob failure is fixed by changing ONLY the affected asset's URL or format — never by
  flattening.** Re-host that one image to a Picsart URL (or convert an unsupported vector to a raster
  *for that element alone*); do **not** rasterize the card, component, or whole screen it sits in to
  make the error go away. Flattening to dodge CSP destroys the layering and is a wrong ad.
- **Audit every node's kind BEFORE composing** — shape / vector / text / photo / video — and author
  each as its native kind (above). This layer-type pass is a required step, not optional; skipping it
  is how shapes and text get wrongly baked.
- **If Figma metadata shows no text but the reference visibly has text, STOP and verify the node/
  frame before authoring — do not proceed on the exported assets.** A `get_metadata` call can land on
  the wrong node (a rectangle, a collapsed instance) or not expand the frame's nested tree, returning
  no text for a frame that clearly contains it. That is a signal to re-inspect the full nested
  structure / correct frame node (`get_metadata` on the right frame, `use_figma` for the tree), never
  to reconstruct from the visible screenshots and omit the text. Missing text in the read = read
  again, don't guess.

- **A composed frame must render identical to the static design — proven by comparison, not
  assumed.** Render one `contact_sheet` frame at the frame's hold start and put it **side by side
  with the source** (`get_screenshot` of the Figma frame). Look specifically for **text that
  clipped, wrapped, or shifted** versus the design (the alignment/box traps above), layers that
  landed off-position, and wrong z-order. Skipping this compare is how a mispositioned or
  wrapped headline ships — it is a wrong ad, cheaply caught now.
- **Compose one frame, PREVIEW it, and wait for the designer before the next — never
  batch-compose the storyboard.** After each frame is composed, actually **render it and show it**
  (the `contact_sheet`/editor still beside the source) and let the designer confirm it before
  you compose the next frame. The preview is a **hard precondition to advancing, not a formality
  you narrate past**: proceeding on your own "it should match" judgment — *without having rendered
  and shown the frame* — is the skipped-preview failure, the same one the pair rhythm below exists
  to prevent. If you catch yourself *describing* a frame instead of *showing* it, stop and render
  it. The designer should never have to ask you to preview — and the reverse holds too:
  **never ask the designer for a go-ahead to render.** A free render (`validate_scene`,
  `contact_sheet`, the editor panel) needs no permission — run it, show the result, and let
  the designer react to what they see. "Approve the render when it pops up" / "say go and I'll
  run it" is a dead turn where the result should already be on screen.
- **You do NOT have to compose the whole storyboard before any motion — compose and motion
  INTERLEAVE per pair.** The stage table lists Compose (2) before Motion (3), but that is the order
  *for a given frame*, not a barrier across the whole reel. The rhythm: **compose frame 1 → animate
  frame 1's opening beat → then, for each pair, compose the next frame only when its pair comes up, and
  motion it.** Composing every frame first and only then starting motion both **delays the motion the
  designer is waiting to see** and contradicts *"open with frame 1's entrance before pair 1→2"* (Stage
  3). Rebuild just-in-time, pair by pair.
- **Matched elements stay one continuous layer across the frames they appear in**, keyed
  by `matchKey` from import — that is what lets Stage 3 smart-animate them rather than
  cross-dissolve. A header present in frames 1–4 is *one* layer with four transform
  states, not four separate headers.
- **Duration is derived by default; an explicit target is opt-in and soft.** By default
  (`duration.mode: "derived"` — the console's checked box) the total is `$sum` of the
  holds, each grown to fit its own motion (a 1.2s type-on needs a hold ≥ 1.2s), so it
  *grows as motion is added*. A designer may instead set `duration.mode: "target"` with a
  length — a **soft budget**: holds scale toward it where there is slack, but never below
  each hold's motion-minimum, so a too-short target **lengthens** the video rather than
  clipping an animation. Never silently clip motion to hit a number; a target the motion
  overflows is shown, not enforced.

- **Every asset URL in the scene must be Picsart-hosted (`pastatic`/`gcdn`) — never a `figma.com`,
  `data:`, or `blob:` URL.** A Figma `get_screenshot` returns a `figma.com/api/mcp/asset/…` URL and
  browsers hand you `blob:` object URLs; **do not author either into the scene.** First lift every
  image to a durable Picsart URL with `picsart_media_upload` (or `picsart_drive`) and author *that*.
  The catch: server-side `export`/`contact_sheet` **can** fetch `figma.com`, so they render fine and
  hide the problem — but the browser preview's Content-Security-Policy blocks these hosts
  (`csp: blocked connect-src … figma.com`, `csp: blocked img-src … blob:`), so the layer goes blank
  or the engine fails. **Validation and a clean contact-sheet are NOT proof it loads in the preview.**
  Fix a blocked scene by re-uploading the offending assets and `patch_scene`-ing their `uri` to the
  Picsart URLs. (Full detail: `references/AUTHORING-FLOW.md` §5.)
- **A text layer's font is always the resolved `.ttf`/`.otf` file URL from `picsart_media_list_fonts`
  (or an `assets[]` `{ type: "font", id, uri }` entry carrying it) — never a family name ("Inter")
  and never a bare catalog key ("gilroy-semibold").** A key *passes* `validate_scene` but the
  editor/exporter engine **fails to boot** on it (no catalog wired → *"cannot read fontLocation…
  font is a NAME"* → the still/video fallback): **validation ≠ boot for fonts.** Match the design's
  typeface to a catalog entry, then author that entry's **URL**, which hands the engine a location.
- **A text layer carries the design's real horizontal alignment and box behaviour — never a
  guessed one.** Author the `alignment` Figma actually set (`textAlignHorizontal`), because the
  anchor follows it: a **left**-aligned layer's `position.x` is the box's **left edge**, a
  **center**-aligned layer's is the box **center**. Defaulting a headline to `center` (and so to a
  center-of-box x) is the classic tell — it drags left-aligned copy off toward the middle/right.
  And if Figma's text sits in a **fixed-width box that overflows the frame**, it **clips** there;
  a scene text layer with only a point anchor + font size will instead **soft-wrap** the overlong
  line at the space. Carry the box width (so it clips like the design) or drop the font size so the
  single line fits — never let it silently wrap to two lines.
- **A missing font is surfaced, never silently swapped.** For every text layer, check its
  typeface against `picsart_media_list_fonts` (the platform's available fonts) — **or
  `picsart_media_discover_fonts`** when `list_fonts` isn't on your connector, or to live-search a
  font not in the bundled snapshot (it returns a `.ttf`/`.otf` URL you author directly). If the
  design's font is **not available from the media tools** — neither tool can find it — do
  **not** quietly substitute a fallback and move on. **Tell the designer plainly**: name
  the missing font, name the fallback you'd otherwise use, and say the text will render in
  that fallback (different metrics → the layout can shift). Let them decide — pick an
  available font, supply/upload the font, or accept the fallback knowingly. A silently
  swapped font is a wrong ad that reads as "close enough" until someone notices.

Then `picsart_media_validate_scene` — free, catches schema errors before any render is
spent on a broken scene.

## Stage 3 — the real motion design (the whole point)

This is where a slideshow becomes an ad. You **author** the motion through
`picsart_media_patch_scene` / the apply tools, then **show** it in `picsart_scene_editor` (the
gen-ai widget tool, **not** a `picsart_media_*` proxy): it plays the pair's MP Scene document in
the browser, free, and the designer approves it or sends it back with edit-request comments (pins
and marked stretches, each with its own instruction). Full contract in
`docs/widget-specs/mp-scene-editor.md` and `references/WIDGET-FEEDBACK.md`; the authoring
craft is in the `motion-design` skill.

**The motion vocabulary** — what "real motion design" is made of, all per-element:

- **Entrances / exits** — fade, slide (from an edge), scale/pop, blur-in, mask-reveal.
- **Continuous** — list **scroll**, **parallax** (layers drift by depth), text **type-on**,
  counters, marquees.
- **Interaction simulation** — the **tap-dot pulse** and cursor moves that make a UI look
  driven (the magenta taps in the reference).
- **Matched-element smart-animate** — a shared layer **morphs its transform/opacity/content
  from frame A to frame B**, so the interface *moves* between screens instead of dissolving.
- **Kinetic type** — headlines animating in word-by-word or with a character stagger; and the
  **assembling sentence**, where shared words *persist and re-position* across frames while new words
  enter (`AI` → `AI Effects` → `AI Effects made easy.`) — not a per-frame re-type, and never a cut
  (recipe: `MOTION-PATTERNS.md` *"Progressive text build"*).
- **Camera** — a gentle push-in/scale on the whole comp for emphasis; or, when a connective
  through-line (a stroke, road, path, ribbon, type baseline — any content, not only a vector) runs
  off one frame's edge and continues into the next, **tile the frames into one world canvas** at the
  offset that makes the line continuous and **travel the viewport along it while the line draws on in
  sync** — one continuous shot, not a cut. (The pan is an **empty-"world"-layer rig**, not a
  `composition.camera` pan, which is a v1 no-op — detection + full recipe in `references/TRANSITIONS.md`.)
- **Brand outro** — the closing logo lockup.

Craft rules: entrances ease **out**, stagger siblings (~0.05–0.12s apart) rather than
firing together, and let the matched elements carry continuity so new elements are the
only things entering at a seam. Depth sells it — background panels drift less than
foreground.

Keep the motion legible: lead with the simplest move that reads (a preset entrance, a matched
smart-animate), tune params only when the designer asks, and drop to raw keyframes only for what a
preset can't express. The designer judges the result on the played scene, not a description.

### Work pair by pair — the default rhythm

**HARD RULE — one pair at a time, and STOP for approval before the next.** Never
author, compose, or review more than **one pair (two adjacent frames, A→B)** in a single
turn. Do **not** run ahead across the whole storyboard, do **not** batch multiple pairs or
all frames at once, and do **not** author the next pair until the designer has **explicitly
approved** the current one. Present one pair → wait → only after the approval comes back do
you advance to the next. Jumping ahead — composing or motioning several pairs before the
first is approved — is the single most common failure this rhythm exists to prevent.

**This pair rhythm is for FRAME SEQUENCES (a Figma storyboard).** Uploaded assets have no frames —
they're arranged into one composition (a **grid, stack, collage, montage, or single focus** — not
one-asset-per-screen by default) and worked by the **working mode the user picked** (part by part, or
the whole motion at once — see *Working mode for uploaded assets*), never by A→B pairs.

**The preview is mandatory and self-standing — never skip it and never wait to be asked.**
"Present" means you **actually call the preview** (`picsart_scene_editor` / `picsart_media_export`)
and show the result; it does **not** mean describing the motion in prose, citing a `validate_scene`
pass, or saying it "should" look right. Advancing without a shown preview — or only previewing
because the designer *reminded* you to — is itself the violation, even if the motion turns out fine.
Every pair ends the same way: **shown preview → explicit designer yes → next.** No shown preview,
no advance; no explicit yes, no advance.

**And the preview that proves MOTION is the editor, not stills.** Contact-sheet / still exports are
fine for the Stage-2 *position* render-match, but the still renderer and the browser editor **draw some
moving things differently** (a photo scaling from its edge looked right in stills yet overlapped a
sibling in the editor). So judge motion on `picsart_scene_editor` (or a played export), never a
still — `references/MOTION-TROUBLESHOOTING.md` §"It looked right in the stills but wrong when it played".

**Approval gates what the designer SEES, never your intention to render it.** Free calls —
`validate_scene`, `contact_sheet`, opening the editor, an export used as a preview — run
without asking: render first, show, *then* wait for the reaction. Never ask "shall I render?",
never report that a preview "needs your go-ahead", and **never tell the designer to type "go"
(or any keyword) to continue** — the designer speaks in design terms ("snappier", "approved",
"change the easing"), not in control tokens. The compose/motion/preview loop is free, so it never
gates on cost. The actions that **do** require an explicit ask are **saving to Drive** and
**generating video** — the latter behind the full cost-consent gate (show credits → preflight cost →
explicit yes; see *Video generation*). Those two aside, act on free calls without asking.

**Open with frame 1's entrance, before pair 1→2.** The reel starts with motion, not a frozen
frame — author frame 1's own elements animating **in** at `t=0` (entrances by role, staggered by
hierarchy), get that opening beat approved, and only then start pair 1→2. A static first frame that
jumps into the first transition is a slideshow tell (full craft in the `motion-design` skill,
*The opening beat*).

**First, analyze the whole frame sequence — a "pair" isn't always two frames.** Before you start
pairing, read the run with the diff tools (`picsart_media_diff_layouts` / `query_layout`, never by
eye) and find the stretches that are really **one motion across several frames**: a **scroll** (frame
k, k+1, k+2… are the same page translated along one axis), a **zoom/pan** (B is A scaled/cropped), or
a **highlight-in-place** (B is A with emphasis). Those runs collapse into a **single continuous beat
over one unit of content** — you animate the whole scroll as one gesture, not frame-by-frame with a
stop on each waypoint (full detection + authoring in [`references/TRANSITIONS.md`](references/TRANSITIONS.md)).
**This is a judgment that highly depends on the case** — how many frames belong to the run, whether
it's truly one scroll or genuinely separate screens, where it should pause — so decide it per design
from the tool-read facts, not from a fixed rule, and confirm on the played result. Everything below
about "pairs" applies to these grouped beats too: one is authored, shown, and approved at a time.

**Then work in the imported Figma order, from the top** — the first beat (frame 1's opening, then
1→2 or the first grouped run), then the next, and so on. "The next" always means the next in
sequence. **Never open with a middle or last beat (e.g. 8→9), and never hop between non-adjacent
ones** — not even when a later frame looks more interesting. Picking out of order is the same failure
as running ahead, pointed sideways: the storyboard is animated from the top, so the designer reviews
it the way it plays.

Author motion **one pair at a time** (the reference was built the same way — screen to
screen):

1. Focus **exactly one pair** — the **earliest pair not yet approved** (start at 1→2): frame A,
   the motion, frame B. Nothing beyond this pair is touched yet. (Open `picsart_scene_editor`
   on this pair to view the motion.)
2. **Propose the motion — author it, don't ask first.** Lead with the best role×energy /
   principle-based suggestion for the pair: each element's entrance/continuous motion **and** the
   matched-element smart-animate carrying shared layers A→B. A rendered proposal on screen is a far
   better prompt than a blank *"how do you want this to move?"* — so author, then show. (If the
   designer *has* stated an idea, author that instead — never impose your proposal over a stated one.)
3. **Show it moving, framed as a proposal** — open `picsart_scene_editor` **on the whole scene
   built so far** (titled for this pair) to play the motion in context (or export the whole reel):
   *"Here's f2→f3 — tell me what to change."* The designer approves or sends it back with comments.
4. **Take direction and revise.** The designer reacts to what they see — "snappier", "drop that
   element", "slower on the CTA". Fold each request into the pair and re-show. Expect a few passes;
   each maps to timing / staggering / element inventory, not a fresh design.
5. **Wait for the designer's explicit approval.** Only after it arrives do you flip that
   pair `approved: true` in `motion.json.seams` and move to the **next** pair. No approval →
   no advance, full stop.

**One decision per turn**, the running cut stays playable, and the **gate is met only
when every pair is approved**. Never self-approve by describing motion — the yes comes
from the designer, on a moving preview. Touch a shared frame and you may change both its
pairs, so re-preview the neighbour.

The panel does not latch: every edit writes a new scene document and `sceneRef` moves —
always edit the latest. And when the editor returns an **`edited` document**, that becomes the
latest — patch further changes onto it, not onto the copy you opened with.

## Video generation — opt-in, and CREDIT-GATED (the one paid capability)

Sometimes the design needs **generated footage** — a video card/element that should be real motion,
or the designer asks to generate a clip with the model they want. This is the plugin's only
credit-spending path, so it is **always gated on the designer's explicit, informed consent.** Use it
only when the design genuinely needs it or the designer asks — never on your own initiative to "fill"
something. Run this flow **in order, and never skip the gate**:

1. **Show the balance FIRST.** Call `picsart_credits` and tell the designer what they have (`balance`,
   plus `nextResetDate` if the pool refills) **before** anything is chosen — this frames every cost
   decision that follows.
2. **Let the designer pick the model.** Narrow with `picsart_media`/`picsart_model_catalog` (plain
   data, no UI), then open **`picsart_model_choice`** with 2–3 candidates that fit the brief (exactly
   one marked `recommended`, each with a `creditsNote`); the pick returns as widget context. Don't
   pick silently when the trade-offs (duration vs resolution vs cost vs audio) are the designer's.
3. **Assemble params** with `picsart_model_params` (the model's schema) — prompt, duration, aspect,
   resolution.
4. **Get the EXACT cost — `picsart_preflight`** (free): it validates the params *and* returns
   `credits`, the dry-run price with no charge. Fix every `errors` entry before going on.
5. **Ask for consent, showing the number.** State it plainly — *"this will cost ~N credits; you have
   M"* — and wait for an explicit **yes**. **No preflight cost shown + no explicit yes = no
   generation.** This is a hard gate: the only credit-spending action in the plugin, never run
   unasked. If `preflight` returns `credits: null` (pricing unavailable), say so and either get
   consent to proceed at unknown cost or stop — never charge silently.
6. **Generate** with `picsart_generate` (video defaults to `async: true` → a job handle; pass the
   project's `folderUid` so the take is filed under this design).
7. **Monitor** with `picsart_render_monitor` on the job(s) — it polls and posts the finished URL back;
   never ask the designer to wait or check.
8. **Place the result** as a `media` layer in the scene (it's real external footage — a legitimate
   video asset, the one uploaded asset that's generated rather than exported from Figma), positioned
   like any other layer, then previewed in the editor for approval as usual.

## Formats — MP Scene is the source of truth

Everything is authored and edited as an **MP Scene** — the format the SDK natively
supports and the one every `picsart_media_*` edit / validate / query tool speaks, and the
one `sceneRef` points at. The **mp4** is *derived* from it at Stage 5 (`export`). The MP
Scene is the single editable source of truth — keep it and edit it; the mp4 is the final
render, never an editing surface.

## The state — `motion.json`

Keep project state in `motion.json`, updated whole at every milestone. **Never print it
in chat** — a request to *see* something opens the panel, not the data.

```json
{
  "project": "ai-playground-ad", "stage": 3,
  "source": { "kind": "figma", "fileKey": "…", "frames": ["node:1:2", "node:1:8"] },
  "target": { "ratio": "9:16", "width": 1080, "height": 1920, "fps": 30 },
  "sceneRef": ".mp-ai-output/scenes/<hash>.json",
  "frames": [
    { "id": "f1", "name": "model picker", "hold": 3.0,
      "layers": [
        { "id": "f1_tabs", "role": "chrome", "type": "group", "matchKey": "tabs",
          "transform": { "x": 80, "y": 120, "w": 920, "h": 96, "opacity": 1, "z": 10 },
          "asset": { "url": "https://…/tabs.png" },
          "motion": [ { "kind": "slide", "from": "top", "at": 0, "duration": 0.5, "easing": "ease_out" } ] },
        { "id": "f1_list", "role": "content", "type": "raster",
          "transform": { "x": 80, "y": 260, "w": 920, "h": 1300, "opacity": 1, "z": 5 },
          "asset": { "url": "https://…/model-list.png" },
          "motion": [ { "kind": "scroll", "axis": "y", "by": -600, "at": 0.6, "duration": 1.8, "easing": "ease_in_out" } ] },
        { "id": "f1_tap", "role": "fx", "type": "vector",
          "transform": { "x": 610, "y": 940, "w": 64, "h": 64, "opacity": 0, "z": 20 },
          "motion": [ { "kind": "tap_pulse", "at": 2.4, "duration": 0.4 } ] }
      ] }
  ],
  "matched": [ { "key": "tabs", "layers": { "f1": "f1_tabs", "f2": "f2_tabs" } } ],
  "seams": [ { "between": ["f1", "f2"], "kind": "smart-animate", "approved": false } ],
  "gates": { "1": "passed", "2": "passed", "3": null, "4": null, "5": null },
  "blocked_on": null
}
```

Why the shape: a **layer** carries its own `transform`, `asset`/`content`, and a `motion`
list; `matchKey` links a layer to its twin in the next frame so `matched[]` can drive a
smart-animate; a **seam** is a `smart-animate` (matched layers morph) rather than a flat
transition wherever elements are shared. `sceneRef` **moves** with every edit — store
the latest.

A layer's **`type`** records what it *is*, which is what `motion-import` decided the export
format from: **`text`** (live text — the string + font, animatable per-line/char), **`vector`**
(icon/logo/shape kept as SVG or a flat fill — stays crisp at any scale), **`raster`** (a photo
or complex image baked to PNG), or **`group`**. Don't record a vector or text layer as a raster
PNG — that's the defaulting mistake that softens the ad at every scale but the one it was baked
at (see `motion-import`).

**`motion[].kind` labels above are DESCRIPTIVE project state — NOT `apply_motion_preset` ids.**
Each is *realized* through a real mp-scene mechanism, and you must map it, not pass the label to the
engine: `slide` → `apply_motion_preset("slide_in")`; `tap_pulse` → **`apply_motion_preset("tap_pulse")`
— this IS a real preset** (verified on current builds; a one-shot dip to ~0.92 and back, `at` = when it
fires, `appliesTo` includes `shape`; older text here said it didn't exist — that's out of date);
`scroll` / `smart-animate` → `position`/`scale`/`opacity` **keyframes** via `patch_scene` (there is no
`scroll` preset). So the label→mechanism map is: some labels are presets (`slide`, `tap_pulse`), some
are keyframes (`scroll`, `smart-animate`) — **resolve every id from `get_capabilities`** rather than
assuming a label is or isn't a preset, and only if your build lacks `tap_pulse` compose it from
`scale_pop` + `opacity` keyframes. Hand-authoring a whole animation "from scratch" when a real preset
(`scale_pop`, `slide_in`, `fade_in`, `tap_pulse`…) fits is the anti-pattern (see *"reach for the
highest-level tool"*); keyframe only the part no preset covers.

## Cost ledger

| Free (everything in this plugin's loop) | Charged, no rollback |
|---|---|
| layer import, compose, `validate`, layout queries, the panel and its motion previews, filmstrip, **and the renders** — `picsart_media_export`, `picsart_media_contact_sheet` (GPU renders that move no credits) | **generative** tools only — image/video/audio generation, remove-bg and kin |

**A dropped call is never a failed render.** An export usually completed even when the
connection dropped — check first (`picsart_job_status`, recent Drive files), never
blind-retry (each retry re-renders and re-saves to Drive), never present a completed
render as an error.

## Traps

- **Binding a frame as a flat PNG when its layers were available.** This is *the*
  slideshow trap, and the reason for the whole layered rewrite. Layers in → elements
  move; flat in → screens dissolve.
- **Not tagging matched elements**, so shared chrome cross-dissolves instead of
  smart-animating — the single biggest tell of "slideshow, not ad".
- **Over-splitting** a design into hundreds of nodes (noise) or **under-splitting** so
  nothing can move independently. A layer is anything that moves on its own or persists
  across frames.
- Firing all entrances together instead of staggering; no depth/parallax, so it reads flat.
- Judging an export from a **still**; editing a **stale `sceneRef`**; losing the scene to
  an **expired CDN link** because nothing was saved to Drive.

Referenced files: 15

motion-design23 KB

View saved version →

---
name: motion-design
description: Stage 3 of the motion pipeline — the craft of turning a composed, layered scene document into a real motion-designed ad. Choose motion per element by role (what scrolls, types on, pops, parallaxes), author matched-element smart-animate between frames, and review it by eye in the scene editor (`picsart_scene_editor`), pair by pair. Called by the `motion` skill.
---

# ChatGPT and Codex integration

Apply these host conventions throughout this skill and its references.

- Discover the connected Picsart tools and use their actual names and schemas.
  The `picsart_*` names in the workflow identify tools, not a fixed host namespace.
  If the connection is missing, explain that Picsart must be connected; continue
  script and shot planning when possible. Never invent tool results or renders.
- Read board decisions from the current user message and attached widget context.
  Use `read_widget_context` only if the host actually exposes that tool. Do not
  assume ChatGPT has Claude's context reader. Treat a board's initial tool result
  and unsent widget state as previews, never as user approval. If a submitted
  decision has no accessible payload, explain briefly and use the text fallback
  to collect the missing decision; do not guess or repeat charged operations.
- Use the host's available question interface or one concise conversational
  question for preset choices.
- UI rendering and delivery of feedback are the remote MCP server's responsibility.
  If a board is unavailable, present its relevant options in text and record the
  user's selection using the same documented feedback fields. Do not claim that
  a plugin manifest makes Claude-specific widgets compatible with ChatGPT.
- Save `motion.json` and artifacts in the user's writable project workspace, not
  the installed plugin directory. If persistent file tools are unavailable,
  keep a structured project checkpoint in the conversation and provide it for
  download when the host supports files. Never claim a file was saved without
  confirmation from a file tool.
- Honor the user's current instructions and already authorized scope. Preserve
  quoted spend limits and production locks; do not infer authorization from a
  preview, a timeout, or a connection being installed. A timed-out charged call
  needs a status check before considering a retry.
- This package holds six film skills (`film`, `film-development`, `film-assets`, `film-scenes`, `film-edit`, `film-finishing`) and three motion skills (`motion`, `motion-import`, `motion-design`). Route between them directly.
- `motion-import` reads a design's layers through the Figma app's tools. If Figma is not connected in this host, use its flat-frame fallback and say so once.


### Motion server compatibility

These host conventions qualify tool and widget instructions below. Discover
live tool names and schemas before invoking the pipeline. A ZIP update changes
instructions; it does not deploy new server tools.

When `picsart_scene_editor` is available, it accepts `scene`, `title` and
`notes` and emits `scene_editor_feedback` v1. Accept without edits or comments
returns `verdict: "approved"`; Submit after edits or comments returns
`verdict: "needs_changes"`. Read the returned `document` when `edited` is true:
frame comments carry `instruction`, and timeline comments carry `text`, `start`
and `end`. Opening the editor is a preview, never approval. If the tool is
absent, use the preview and text-review fallback already described in this skill.

Dedicated import/gradient/tracking tools in the new workflow may require a
newer MCP deployment. Call them only when exposed. Use a documented live
recipe or the source's explicit fallback when it supports the same operation;
otherwise explain the missing capability before proceeding with that step.
Keep the original editable assets and the current scene intact.

# Stage 3 — authoring the motion

A composed scene is a set of layered frames that render identical to the design and don't
move yet. This stage makes them move — and the gap between "slideshow" and "ad" lives
entirely in the choices made here: **which elements move, how, and what carries between
frames.** The design is authored as an MP Scene throughout; every edit is a
`patch_scene` that writes a new scene document (`sceneRef` moves — always edit the latest).

The rule under everything: **motion draws the eye, so it is spent, not sprayed.** A frame
where everything animates at once reads as noise. Pick the focal element per beat, move
it, let the rest support.

## Read the design before animating it

**First, read what changed** — from tool facts, not by eye. Diff the pair with
`picsart_media_diff_layouts` (pass frame A, frame B, and the import `matchKey`s as `matchHints`);
it classifies each layer as moved / resized / restyled / content-changed / entered / exited /
unchanged with root-space deltas. If the tool is unavailable, fall back to `picsart_media_query_layout`
on A *and* B and diff by hand. Either way, **check fades yourself** — the diff sees only `visible`,
not numeric opacity, so an opacity change reads as unchanged. **Animate only what
changed; leave unchanged layers still.** The full frame-diff procedure and the
change→motion/easing/duration/reduced-motion mapping live in
[`../motion/references/MOTION-PRINCIPLES.md`](../motion/references/MOTION-PRINCIPLES.md).

Then choose each moving element's motion by its **role**, not at random. This is the default
mapping — a starting point the designer overrides in the panel, not a law:

| Role (from import) | Default motion |
|---|---|
| headline / display text | **type-on**, or word/character **stagger** in |
| body / label text | short fade + slide-up |
| CTA / button | **scale/pop**, entering *late* (after its context), often with a tap-dot |
| hero image | subtle **scale-in / push-in**, or a mask **reveal** |
| list / feed | the content **scrolls**; rows **stagger** in |
| card / panel | **slide** from the nearest edge + fade |
| chrome (tabs, header, nav) | usually the **matched element** — persists and smart-animates; enters first, exits last |
| icon / small accent | quick **pop**, subtle |
| background | least motion — a slow **parallax** drift or gentle scale |

The design's own **hierarchy is the motion priority**: what the eye should hit first gets
the entrance; supporting elements follow.

## How finely to split — respect the design's units, don't take a mark apart

"Split into pieces that move independently" means **use the layers the design has** — not
manufacture finer structure inside a single element. Going finer than the design (splitting one
vector/instance into sub-parts to animate them separately) is the same class of overreach as
inventing a cursor or a ripple: you're adding structure the designer didn't author.

**Default: animate each element as the one unit Figma authored** (one layer / instance / vector =
one unit). Sub-dividing that unit is a bigger claim — allow it **only when all three hold**:

1. the sub-parts are **distinct content** — separate things — not the strokes/segments of one glyph
   or mark;
2. the **concept calls for it** — a set, variety, or a progressive build the message depends on;
3. it is **not the brand's identity** — a logo, wordmark, or **app icon** stays whole regardless of
   how separable its parts look.

**When it's a judgment call, keep the unit whole and OFFER the split** — let the designer choose;
never impose decomposition. *Worked example:* an AI-Playground app icon whose mark is a square,
sparkle, diamond and circle **fails #3** — it's the identity mark — so the open is a whole-icon
reveal (fade/scale the tile+shapes in together), and "the four shapes pop in one by one" is offered
as an option, not authored by default. Taking a brand mark apart on screen reads as *disassembling
the logo*, which is rarely what the designer meant.

## The opening beat — frame 1 animates IN, it never just sits there

**The reel opens with motion, not a frozen screen.** A static frame 1 that holds and then
suddenly jerks into the 1→2 transition reads as amateur — the viewer sees a still image, then a
jump. Instead, **frame 1's own elements animate in at t=0**, so the first thing on screen is the
design *assembling itself*, and by the time the 1→2 motion fires the frame already feels alive.

- **Entrances by role, staggered by hierarchy.** Same role×energy vocabulary as any entrance
  (`MOTION-PRINCIPLES.md`): background/chrome settle first, then the hero/headline, then the CTA
  last. What the eye should hit first enters first; siblings stagger ~0.05–0.12s, never in unison.
- **It's an intro, not a showcase — keep it tight.** A quick, confident assemble (energy scales
  it: energetic → snappier and shorter; calm → a softer fade-up). Don't animate *everything*; a
  couple of focal entrances plus a gentle settle on the rest reads better than a busy cascade.
- **Then hold, then transition.** Opening beat → the frame's content-derived hold (the "no dead
  air" rule still applies) → the 1→2 motion. The opening is a distinct beat *before* pair 1→2.
- **Nothing is matched yet** (there's no frame before frame 1), so this beat is entrances only —
  no smart-animate, no exits.
- **Don't manufacture parts to stagger.** A sparse open (one icon on a dark ground) tempts you to
  invent motion by taking the one element apart. Whether that's allowed is a real judgment — see
  *How finely to split* below — but the default on a brand/app icon is a **whole-icon reveal**, with
  a staggered build only **offered**, never imposed.
- **Symmetric at the end:** the last frame shouldn't freeze-frame either — let it settle/hold on a
  deliberate final state (or a brand lockup) rather than snapping to a stop.

Author this as frame 1's entrance animations at `t=0`, self-check it like any pair, and get it
approved as the reel's open **before** you start pair 1→2.

## Matched-element smart-animate — the technique that makes it an ad

This is the single thing that separates motion design from a slideshow. When a layer
carries a `matchKey` into the next frame (tagged at import), you do **not** cross-dissolve
the two screens — you **animate the shared layer from its state in A to its state in B**:

- **Move the delta** — position, size, opacity, and content (a text string changing) from
  A→B, ~0.4–0.8s, **ease-in-out**.
- **Keep matched layers on screen through the transition** — never fade the shared chrome;
  its staying put *is* the continuity.
- **Elements only in A exit** (fade/slide out) as or just before the move.
- **Elements only in B enter** *after* the matched layers have arrived, so the eye follows
  continuity into the new content rather than being asked to look everywhere at once.

Miss this and shared chrome cross-dissolves — the biggest tell of "slideshow, not ad."

## Craft rules that make motion read as designed

- **Easing:** entrances `ease_out` (fast, then settle), exits `ease_in`, matched moves
  `ease_in_out`. Reserve overshoot/spring for a playful register; clean eases read
  professional.
- **Duration scales with the change:** bigger travel/scale → longer motion, but stay short
  (~200–400ms typical; >400ms drags). Scale by energy — energetic tighter, calm longer
  (see [`MOTION-PRINCIPLES.md`](../motion/references/MOTION-PRINCIPLES.md) §3).
- **Pace to the destination's content load — a busy frame is entered SLOWLY.** The viewer must have
  time to actually see what arrives, at least the important parts. A move into a **dense** frame — a
  grid, many cards, lots of new elements — takes a **longer, wider-spaced stagger** and a **longer
  hold** than a move into a sparse one; a fast cut into a busy screen reads as a blur and nothing
  registers. A sparse destination gets a quicker pass. **Density can override the energy register:**
  "energetic" sets the character, but a packed frame still needs enough time to land — never sacrifice
  legibility for snappiness. *Example:* frame 1 is a single icon → frame 2 is a 6-card
  content grid, so the cards stagger in over a generous window (not a fast simultaneous pop) and
  frame 2 holds long enough to take all six in. (Works with *Hold to the frame's content* below.)
- **The page-CHANGE and the content REVEAL are two separate clocks — one can be fast while the other
  isn't.** When the seam is a **UI action** (a button/tab gets tapped and that *causes* the next
  screen), the **navigation move itself can be fast and snappy** — that responsiveness is exactly
  what makes it feel like a real app: press → the next screen is just *there*. A slow nav on an
  action-response reads as laggy, not cinematic. What still paces to density is the **arriving page's
  content**: after the screen lands, its items animate in with the staggered, legible entrances their
  number demands (a busy page = separately-animated items over a generous window; a simple page =
  little or nothing). So: **fast page-change ≠ fast content reveal** — a quick nav in, then content
  that settles at a speed matched to what's on the page.
- **Scroll speed is content-driven — there is no fixed value; pace it to what the viewer needs from
  the pass.** Two ends of the range, and most scrolls sit between:
  - **Scroll to READ** — the content going by *is* the point (the viewer should register what passes)
    → **slower**, legible; don't outrun the eye.
  - **Scroll to ARRIVE** — the scroll is just transit past simple/known/repetitive content to land on
    and focus **one** item → **fast through the list, then decelerate and settle** on the target
    (ease-out into the focal item; a natural flick-and-settle, not a constant-speed crawl).
  - **The go-to velocity arc (fits most scrolls): slow-in → accelerate → settle.** *Start* the scroll
    slow so the viewer registers **what the list/grid is** (its layout, what kind of content it holds),
    then **speed up** through the bulk once they've got the idea, then **decelerate and settle** on
    where it lands. A scroll that starts fast skips the "what am I looking at" beat; the slow lead-in
    gives it. It's an ease-in-out with a settle — reach for it by default. **Why: a constant-speed
    (monotone) scroll is boring** — it reads as flat and mechanical; the changing velocity is what
    keeps it alive and feels authored. **Exceptions where it
    doesn't apply:** a very **short** scroll (a couple of items — no room for three phases, just a
    quick move + settle); a **simulated UI flick** (a tap-driven scroll meant to look like a real
    thumb starts *fast* and decelerates with friction — the opposite start); and **pure get-to-X
    transit** past known content (fast-through with just the settle, skip the slow lead-in).
  A content-rich list scrolls slower than a simple one being skimmed to a target. It's a judgment on
  the content and the intent (browse-and-read vs. get-to-X), not a number to memorize — decide it per
  case and confirm on the played result.
- **Stagger, don't synchronize:** sibling elements enter ~0.05–0.12s apart, not together.
  A row of cards popping in unison looks mechanical; a stagger looks authored.
- **Depth sells it:** assign parallax by z — background drifts least, foreground most; on a
  push-in the background scales less than the foreground.
- **One focal motion per beat:** establish → (optional interaction) → smart-animate → the
  new element settles. Not five things at once.
- **Hold ≥ motion:** a frame's hold must cover the motion authored on it — a 1.2s type-on
  needs the frame to hold ≥ 1.2s (the compose step derives this; don't fight it).
- **Hold to the frame's content, then move — no dead air.** Beyond that floor, the hold is
  **derived from the frame's own context**: how much there is to read or absorb — a text-heavy
  frame needs reading time (roughly its words at a comfortable pace), a sparse or purely
  transitional frame needs only a brief beat — scaled by the film's energy (energetic →
  tighter, calm → longer). Hold long enough to cover the motion AND let the eye land, then cut
  to the next motion. A frame that keeps sitting static well past its motion and its read is
  **dead air** — it reads as a stall, not a pause (the "we transitioned to frame 2 and it just
  sits there" problem). Rhythm: motion → land → brief settle → next motion, never a long empty
  stare between beats. If the reel overshoots the target duration, trim the **holds**, not the
  motion. **Default to a tight hold — the burden is on the long one:** a frame with nothing
  that needs dwelling on (no text to read, no beat that needs to land) gets only a brief pass,
  never padding. Length must be **earned** by the frame's content, not granted by default.
- **The whole reel should read as ONE continuous velocity — carry it across every beat.** A template
  feels authored when it moves at a **coherent, single velocity/rhythm from start to finish**, each
  beat flowing into the next, not a string of disconnected fast/slow chunks bumped together. That one
  flow is the through-line; individual beats vary around it only for a deliberate accent, then return
  to it. The mechanism is simple: **carry velocity across beats — don't launch a fast move out of a
  slow settle.** The end-speed of one beat should roughly set the start-speed of the next. When a beat
  **decelerates to rest** (a scroll easing to a stop, an element settling), it has brought the eye to
  near-zero speed; snapping straight into a **fast** next animation is a whiplash that **breaks the
  continuity the settle just created** — it reads as a hard restart, not a flow. After a settle, ease
  **gently** into what follows (start it slow, matching the low speed you landed at) or give a brief
  hold first; save a fast next move for when the previous beat handed it real momentum, not from rest.
  A big speed mismatch either way (slow→fast or fast→slow) reads as a bump; keep the seam smooth
  unless you're spending a deliberate accent. (This is the velocity half of the Vector Law —
  `references/TRANSITIONS.md`.)
- **Interaction simulation** belongs *before* a state change it appears to cause — the press
  fires, then the matched-animate to the next state. **Default press = the element itself pulses**
  (the **`tap_pulse` preset** — `apply_motion_preset("tap_pulse")`, verified on current builds: a one-shot
  dip to ~0.92 and back, `at` = when it fires, `appliesTo` includes `shape`; resolve from
  `get_capabilities`, and only if your build lacks it compose from `scale_pop` + `opacity` keyframes —
  applied to every piece of the pressed element with the same anchor) — never an invented overlay: no
  added dots, cursors, or ripples
  that aren't in the design (designers spot invented layers immediately). A **visible tap-dot is
  opt-in**: only when the designer asks or the design language already includes one.
- **Reduced-motion variant (offer, don't auto-make):** for an accessible cut, decorative motion
  (parallax, pulses, big scale-for-flair) is dropped and meaningful motion (matched chrome, state
  changes) is replaced by a dissolve/fade — never removed. Designer-initiated at Stage 5
  (see [`MOTION-PRINCIPLES.md`](../motion/references/MOTION-PRINCIPLES.md) §7).

## The authoring loop — pair by pair, in the panel

Author one pair at a time (see the `motion` skill's pairwise rhythm). You **author** the motion
through `patch_scene` / the apply tools, then open `picsart_scene_editor` **on the whole scene
built so far** (titled for the current pair) so the designer watches it play *in context* and scrubs
to the pair — not a 2-frame sub-scene — rather than exporting a video every time; reserve
`picsart_media_export` for the final deliverable and the error fallback. For each pair A→B:

0. **Propose first — don't ask up front.** Author the best role×energy suggestion for the pair
   (steps 1–4 below) and show it as a proposal; the designer then reacts with what to change. A
   rendered proposal is a better prompt than a blank "how do you want this to move?" question.
   (If the designer *has* stated an idea, author that instead — never impose your proposal over a
   stated one.)
1. **Per element in A** — choose its motion by **role and the film's energy** (the vocabulary in
   this skill), authored through `apply_motion_preset` / `patch_scene`. Lead with the simplest move
   that reads; tune params only when asked; raw keyframes only for what a preset can't express.
2. **Author the matched-element smart-animate** for every shared layer (A→B delta).
3. **B's new elements** — give them entrances, staggered, after the matched layers land.
4. **Interaction sim** — where the transition reads as UI-driven, press the trigger element
   (`tap_pulse` on the element itself; a visible tap-dot only if the designer opts in).
5. **Play the pair in the editor** (free), and let the designer **approve** — never
   self-approve by describing it. On approval, `approved: true` on that pair; advance.
   Open the editor **without asking first**: a free preview needs no go-ahead, so never
   stop at "the render needs your approval" or ask the designer to type "go" — show the
   result and let them react to it. Approval is for what they see, not for your next
   free tool call.

Each edit is a `patch_scene` → a new `sceneRef`. Read the `scene_editor_feedback` payload
(contract in `docs/widget-specs/mp-scene-editor.md`): `verdict: "needs_changes"` carries
`comments` (frame pins with `instruction`) / `timelineComments` (stretches with `text`, `start`
and `end`), which you fold into the motion via `patch_scene`; `verdict: "approved"` advances the
conveyor. When `edited` is true, use the returned `document`, not your prior copy. If both comment
sets are absent on `needs_changes` and `edited` is true, the returned document is the requested
change. Ask for clarification only when there is no edit or usable requested change.

## Suggesting motion

When you propose candidates, choose by **role × energy**, not a fixed list: a headline at
`energetic` gets a fast word-stagger; at `calm`, a soft fade-up. Preview each **on the
real element**, never as a named list of presets in prose — a designer judges motion by
watching it, and a still or a word ("fade_in") approves things that are wrong.

## Anti-patterns (the slideshow tells)

- **A static first frame** — the reel holds on a frozen frame 1, then jumps into the 1→2 motion.
  The open should animate in (see *The opening beat*); a still that suddenly moves is a slideshow tell.
- **Taking a mark apart** — splitting one vector/instance (a logo, an app icon) into sub-parts to
  stagger them, when the design authored it as one unit. Whole by default; offer the build (see
  *How finely to split*).
- **Everything moving at once** — no focal point; reads as noise.
- **Matched chrome cross-dissolving** instead of smart-animating — the headline tell.
- **No depth** — every layer moves at the same rate, so it reads flat.
- **Synchronized entrances** — siblings firing together look mechanical.
- **Over-animation** — motion on elements that should stay still, fighting the design's
  hierarchy.
- **Judging from a still** — a transition is invisible in one frame; always preview the
  moving stretch before approving.
motion-import22 KB

View saved version →

---
name: motion-import
description: Stage 1 of the motion pipeline — bring a design in as LAYERS, not flat frames. Pulls each Figma frame's layer tree (elements + transforms), captures each layer in its NATIVE format (live text, vector, or raster — PNG is a fallback, not the default), tags elements that recur across frames as matched, gets every asset onto a fetchable Picsart-hosted URL, and sets the video format (including output scale/sizes up front). Falls back to flat-frame ingest only when layers cannot be had. Called by the `motion` skill; not a standalone entry point.
---

# ChatGPT and Codex integration

Apply these host conventions throughout this skill and its references.

- Discover the connected Picsart tools and use their actual names and schemas.
  The `picsart_*` names in the workflow identify tools, not a fixed host namespace.
  If the connection is missing, explain that Picsart must be connected; continue
  script and shot planning when possible. Never invent tool results or renders.
- Read board decisions from the current user message and attached widget context.
  Use `read_widget_context` only if the host actually exposes that tool. Do not
  assume ChatGPT has Claude's context reader. Treat a board's initial tool result
  and unsent widget state as previews, never as user approval. If a submitted
  decision has no accessible payload, explain briefly and use the text fallback
  to collect the missing decision; do not guess or repeat charged operations.
- Use the host's available question interface or one concise conversational
  question for preset choices.
- UI rendering and delivery of feedback are the remote MCP server's responsibility.
  If a board is unavailable, present its relevant options in text and record the
  user's selection using the same documented feedback fields. Do not claim that
  a plugin manifest makes Claude-specific widgets compatible with ChatGPT.
- Save `motion.json` and artifacts in the user's writable project workspace, not
  the installed plugin directory. If persistent file tools are unavailable,
  keep a structured project checkpoint in the conversation and provide it for
  download when the host supports files. Never claim a file was saved without
  confirmation from a file tool.
- Honor the user's current instructions and already authorized scope. Preserve
  quoted spend limits and production locks; do not infer authorization from a
  preview, a timeout, or a connection being installed. A timed-out charged call
  needs a status check before considering a retry.
- This package holds six film skills (`film`, `film-development`, `film-assets`, `film-scenes`, `film-edit`, `film-finishing`) and three motion skills (`motion`, `motion-import`, `motion-design`). Route between them directly.
- `motion-import` reads a design's layers through the Figma app's tools. If Figma is not connected in this host, use its flat-frame fallback and say so once.


### Motion server compatibility

These host conventions qualify tool and widget instructions below. Discover
live tool names and schemas before invoking the pipeline. A ZIP update changes
instructions; it does not deploy new server tools.

When `picsart_scene_editor` is available, it accepts `scene`, `title` and
`notes` and emits `scene_editor_feedback` v1. Accept without edits or comments
returns `verdict: "approved"`; Submit after edits or comments returns
`verdict: "needs_changes"`. Read the returned `document` when `edited` is true:
frame comments carry `instruction`, and timeline comments carry `text`, `start`
and `end`. Opening the editor is a preview, never approval. If the tool is
absent, use the preview and text-review fallback already described in this skill.

Dedicated import/gradient/tracking tools in the new workflow may require a
newer MCP deployment. Call them only when exposed. Use a documented live
recipe or the source's explicit fallback when it supports the same operation;
otherwise explain the missing capability before proceeding with that step.
Keep the original editable assets and the current scene intact.

# Stage 1 — Import: the design in as layers

Motion design animates *elements*, so the import's whole job is to bring the design in as
**layers, not flat frames**. A frame delivered as one sealed PNG can only ever
cross-dissolve; a frame delivered as its layers — panels, headline, buttons, the image,
the list — is a thing whose elements can slide, scroll, type on, parallax and
smart-animate. **Everything downstream is gated on this**: if the layers are lost here,
no amount of tuning later can recover the ad; it stays a slideshow.

## Pull the layer tree, per frame

**With the Figma connector present**, read structure before pixels: `get_metadata` /
`get_design_context` to walk each frame's node tree, then export each animatable node as
its own asset. For every layer capture:

> **AUDIT EVERY NESTED LAYER — never compose from Figma's simplified, frame-level output.** The
> single biggest way items go missing from the ad: Figma's frame-level / summarized read (a shallow
> `get_design_context`, or a `get_metadata` that stops at the top level) **collapses nested children**
> — groups, component instances, auto-layout children, masked contents — into a summary. Take that at
> face value and **every nested element is silently dropped**, so the composed frame is missing pieces
> that were plainly in the design. **Recursively expand each frame's FULL tree** (`get_metadata` on the
> frame node with its children expanded, or `use_figma` to walk *all* descendants) and audit it
> **node by node** — do not trust a frame-level layer list to be complete. **Completeness check:** the
> layers you capture must match the count of leaf/animatable nodes in the *fully-expanded* tree, not
> the handful the frame-level view showed. If a group or instance shows as one node, open it — its
> children are the layers. (Downstream this is the Stage-2 "layer count ≈ node count" gate; catch it
> here, at the read, not after a frame composes short.)


- **id** and **name/role** (what it is — `chrome`, `headline`, `cta`, `image`, `list`, `fx`)
- **type** — `text`, `vector` (icon/logo/shape), or `raster` (photo/complex image); plus `group`
- **transform** — `x, y, w, h, rotation, opacity`, and **z-order** (front-to-back)
- **parent/group** — so a group can move as one
- **for a text layer, also the type properties** — the **string**, resolved **font URL** + size,
  **horizontal alignment** (read Figma's `textAlignHorizontal` — `left`/`center`/`right`; *never*
  guess it from position or from "it's a big headline"), and the **box sizing/wrap**: a Figma
  **fixed-width** text box that runs past the frame **clips, it does not wrap** — record the box
  width and that it clips, or the renderer soft-wraps the line into two downstream.

### Export each layer *as what it is* — PNG is the fallback, not the default

The layer's **source type** decides the export format. Deciding this per layer is the job;
reaching for PNG on every element is the freelance-default that quietly wrecks quality —
**stop and ask "is this layer actually raster?" before you export a PNG.**

- **Text** → keep as **live text** (the string + resolved font URL), *never* an image. This is
  what makes type-on, per-line and per-character animation possible; baking text to PNG kills
  all of it and can't reflow.
- **Vector** — icons, logos, line art, flat shapes → keep **vector**: export **SVG**, or keep a
  flat `#rrggbb` fill as a fill (no asset at all). **Default: turn the SVG's paths into a live
  shape layer** with `picsart_media_import_svg_path` (one call per `<path>` `d`, carrying its
  fill/stroke/opacity). A live shape needs no hosting, is never CSP-blocked, and can recolor and
  draw on. Keep the SVG itself as an uploaded asset only when it holds what paths can't express
  (filters, masks, embedded images/text). Vector stays crisp when it scales/pops and is lighter to
  move. Rasterize to PNG **only** if the node genuinely can't be had as vector.
- **Raster / photographic** — the hero image, a photo, a gradient/blur that won't survive as
  vector → **PNG with transparency** so the layer floats over what's behind it.

If you're about to export a PNG of an icon, logo, or shape, that's the tell you *defaulted*
instead of decided — go back and take it as vector.

**Settle output scale in the first five minutes, not the fiftieth.** Whether the ad ships at
one size or several (multiple ratios/resolutions, or any later up-scale) is what makes vector
the *normal* case, not an edge case — a PNG baked at one scale is soft or jagged at any other,
while vector is resolution-free. Decide this up front (see *Set the format* below); don't
discover the constraint at export time and re-import. And when you do capture
a vector layer, **declare it `type: "vector"`** — silently recording an SVG as `type: "image"`
is the same defaulting mistake wearing a different hat.

**Format doesn't excuse hosting:** a vector converted to a live shape needs no asset at all. But
whatever you keep as a file, the asset still has to be lifted onto a
**Picsart-hosted `https://` URL** (below). A `figma.com` SVG URL is CSP-blocked in the preview
exactly like any other figma.com asset — "keep it vector" does **not** mean "author the
figma.com link." Export vector → upload → author the returned Picsart URL.

### Granularity — split to what moves

Not every node, and not one blob. **A layer is anything that either moves independently or
persists across frames.** Group the rest.

- Split out: the headline, each CTA, the hero image, a scrollable list, a tap target,
  anything that will animate on its own.
- Keep grouped: static decoration, a card's fixed internals, backgrounds.
- Over-splitting (hundreds of nodes) is noise the designer must wrangle; under-splitting
  (the whole screen as one image) is the slideshow trap in disguise. When unsure, split
  by what the reference ad would animate.

### Import every element the design shows — visibility is the design's call, not yours

Import **every element the design renders**. A layer is dropped for exactly one reason: the
**design itself hides it** (Figma `visible:false` / a `hidden` node) — that is the designer's
signal, and even then a *prominent* hidden layer is worth a note, since it may be meant to appear
mid-animation. **Never drop a layer because *you* judged it "won't be seen":**

- **Partially on-frame** (clipped by the frame edge) is **on screen** — it shows in the very first
  hold, so omitting it is a visible hole against the design. This is the frame-6 tell: the search
  pill and the tag row are *clipped, not absent*, and dropping them leaves the composed frame
  missing content the mockup plainly shows.
- **Fully off-frame is not a licence to drop it either.** In motion a wide row or an off-edge
  element is usually there to **scroll or pan into view** — import it and let choreography decide,
  don't pre-empt the motion by deleting it (that is how a design's intended scroll becomes a
  static gap).

A judgment call to leave something out is **surfaced to the designer, never silent** — same rule
as a missing font. The Stage-2 `get_screenshot` compare is what catches a dropped element, as a
hole where the design has content.

## Tag matched elements across frames

The thing that makes an interface *move* between screens instead of dissolving is a
**matched element** — a layer present in consecutive frames that should animate from its
state in A to its state in B. So as you import, give any recurring layer a stable
**`matchKey`** (the same key on the header in every frame it appears in). Match by
name/role first, geometry second. `motion.json.matched[]` is built from these keys, and
Stage 2 keeps each matched element as **one continuous layer**, not a fresh copy per
frame. Miss this and the compose step can only cross-dissolve — the biggest "slideshow"
tell there is.

**A recurring element keeps its `matchKey` even when it is transformed or its content changed** —
a title that moves and shrinks, a headline whose wording grows (`AI` → `AI Effects`), a button that
recolours. Match by **semantic role** ("the title", "the CTA"), *never* by exact string or position:
`AI` and `AI Effects` are the same title. The fact that the same element is **present in both frames
but different** is the *strongest* match signal there is — it means the element should **animate from
A to B**, and it is precisely the case where matching must not fail. A same-role text or object that
is merely repositioned/resized/reworded must **never** be tagged as one element leaving and a
different one arriving; that misread is what makes it disappear-and-reappear (or get papered over
with a transition) downstream instead of moving.

**Matching is at the layer level — so an assembling sentence needs the layers split to match.**
The rule "a header in frames 1–4 is one layer with four transform states" is right for chrome, but
it breaks for text that *grows*: frames whose text reads `AI` → `AI Effects` → `AI Effects made
easy.` are usually **one whole-string text node each**, i.e. three differently-named layers — so
nothing marks `AI` as persisting; the thing that actually persists is a **substring**. Capture it
that way: split the shared words into their **own layer** carried by one `matchKey` across the
frames, and each added clause into its own layer that `enter`s. A single whole-string node per
frame gives the diff three different strings and no persisting layer, and the assembling effect
collapses to a cut. (This is the import half of `MOTION-PATTERNS.md` *"Progressive text build."*)

The A→B **delta** itself (how far a matched layer moved, what actually changed) is not judged
by eye at motion time — it is **read with `picsart_media_diff_layouts`** (which resolves both
frames through `query_layout` and returns the classified delta; `query_layout`-on-both is the
fallback when the tool is unavailable). The `matchKey` you set here is exactly what that diff
takes as its `matchHints` to pair the layers up — a dropped key becomes a missed match there.
The full frame-diff procedure is in
[`../motion/references/MOTION-PRINCIPLES.md`](../motion/references/MOTION-PRINCIPLES.md) §1.

## Get every asset onto a real URL

A Figma export and a `file://` path are both **unfetchable by the renderer**. So each
layer asset that is not already an `https://` URL must be lifted onto one: `picsart_media_upload`
(the drag-and-drop panel) or `picsart_drive`, then **read back the resulting `https://` URL and
author *that*.** Text and fills carry no asset and need no lift.

**A scene asset's `uri` must be a Picsart-hosted `https://` URL (`pastatic`/`gcdn`, from
`picsart_media_upload` / `picsart_drive`) — never a base64 `data:` URI, and never a `figma.com`
URL.** The interactive **preview engine runs in a browser with a Content-Security-Policy** whose
`connect-src` allowlist excludes both — they get **blocked → blank** in the editor preview
(`csp: blocked connect-src … figma.com/….svg`), even though the scene *validates* and the
server-side `export`/`contact_sheet` render them fine. That server/browser split is the trap: a
`figma.com` URL or a `data:` URI is only ever an **intermediate** — hand it to `upload`/`drive` and
author the **Picsart-hosted URL it returns**. **`validate_scene` and a clean `contact_sheet` do NOT
prove it renders in the preview** (both are lenient server-side paths) — the only reliable proof is
the editor preview itself, so default to Picsart-hosted URLs and it's fine everywhere.

## When layers cannot be had — the honest fallback

Two cases force flat-frame ingest, and both are a **degrade, announced**:

- **No Figma connector.** Suggest it once, and offer the fallback in the same turn —
  never block, never re-ask, and you cannot connect it for them:
  > Connecting the Figma connector (Settings → Connectors → Figma) lets me pull your
  > design's layers, which is what makes real motion design possible. Without it I can
  > only animate whole flat frames — a slideshow. Share the Figma file, or paste flat
  > frame images to do the simpler version.
- **Figma export quota'd out.** Figma export is metered **per Figma seat** and can quota
  out mid-batch — that is **status, not an error**: continue on the upload path for the
  remaining assets, and if only flats are reachable, say the result will be a slideshow
  and offer to resume layered when the seat's quota returns.

A flat frame is imported as a single `image` layer filling the composition — the montage
fallback in the `motion` skill. Say plainly what is lost: *"without the layers this is a
slideshow, not motion design."*

## Order — take Figma's, never ask for it

The running order is **Figma's own** — the frame/artboard sequence in the file (or, for
pasted images, the order they were given in). **Take it as authoritative, record it, and
move on — do not ask the designer to confirm it.** A question the source already answers is
noise — the house rule: *"don't turn something the pipeline
already knows into a user choice."*

Reordering is **optional and designer-initiated**: it happens on the storyboard board
(shown for a glance and a drag, never a mandatory confirm) or when the designer explicitly
says "swap 2 and 3." Absent that, Figma order stands, silently. Never open an
the host question interface to confirm the running order.

## Set the format — inferred directly (no setup widget)

Decide the video-wide format yourself from the material — **infer it, state it, and proceed**;
this workflow does not require the `picsart_motion_setup` console, which production may expose. Choose:

- **ratio / resolution** — from the frames' own aspect (1080×1920 → 9:16 at 1080×1920). Take what
  the design already is.
- **fps** — 30 by default.
- **energy** — read from the design (see below); pick the register the ad calls for.
- **output scale/sizes** — default to a single size, but **surface the question when it's genuinely
  open**: will this ship at one size or several (multiple ratios/resolutions, or a later up-scale)?
  The answer decides raster-vs-vector for every layer above, and "multiple sizes" makes vector the
  norm — cheap to settle now, expensive to discover at export. When it isn't obvious, a plain
  the host question interface is the right surface; otherwise state the single-size default and move on.

State the chosen ratio/fps/energy to the designer in one line and let them adjust in plain
conversation ("make it square", "60fps", "more energetic") — a normal message, not a widget.

**`energy` is a productive↔expressive dial**, not just "fast vs slow": *productive* =
subtle, efficient motion for routine beats; *expressive* = vibrant, highly-visible motion for
significant moments (an opening, a primary CTA, a hero reveal). It scales the motion's duration
and easing register downstream — see
[`../motion/references/MOTION-PRINCIPLES.md`](../motion/references/MOTION-PRINCIPLES.md) §6.

Two things are never asked: **order** (taken from Figma silently, above) and **total duration**
(derived from the motion, never a target; a designer who wants it tighter says "punchier").

## Output → `motion.json`

Write frames-of-layers and the target (schema in the `motion` skill). Each frame carries
a `layers[]` with `id`, `role`, `type`, `transform`, `asset`/`content`, and `matchKey`
where it recurs; `matched[]` is assembled from the keys.

## Gate to pass Stage 1

Print it with ✓/✗ before compose:

- Every frame imported as **layers** (or consciously flagged flat-fallback with the
  reason recorded).
- Every animatable layer has an **asset at a real `https://` URL** (or live text/fill)
  **and a transform** with z-order.
- **Every element the design shows is imported** — nothing dropped on a self-made "won't be
  visible" call; a partially-clipped or off-frame layer is still imported. Only designer-hidden
  layers (`visible:false`) are omitted, and a prominent one is noted.
- **Matched elements tagged** with `matchKey` across the frames they appear in.
- Each layer captured **in its native format** — text is live text, icons/logos/shapes are
  `type: "vector"` (SVG/fill), only genuinely raster content is PNG. No vector or text baked to
  PNG by default.
- **Order** recorded, **target format** set, and **output scale/sizes** settled.

A frame that came in flat while its layers were available is a **gate failure, not a
shortcut** — the whole pipeline's value is here.

## Mistakes

- Exporting flat frames when the Figma layers were right there — the slideshow trap at
  its source.
- **Silently dropping a design element because you judged it "won't be visible."** A
  partially-clipped element is *on screen now*; a fully off-frame one usually *scrolls in*. Only a
  designer-hidden layer is a legitimate skip — and even that is surfaced if prominent, never a
  silent judgment call.
- Losing transforms/z-order, so composed layers stack or land wrong.
- Not tagging matched elements, forcing cross-dissolves later.
- Over- or under-splitting: hundreds of nodes to wrangle, or one blob that cannot move.
- **Flattening a vector or text layer to PNG by default** — text should stay live, icons/logos/
  shapes stay vector; PNG is only for genuinely raster content. Reaching for PNG per element is
  the freelance-default that softens the ad at any other scale.
- **Declaring an SVG layer `type: "image"`** — recording vector as raster is the same default
  wearing a different hat; it loses the resolution-free path.
- **Dropping a text layer's alignment or box behaviour** — capturing only `x/y/w/h` and then
  guessing `center`, or losing the fixed-width-clip box so the line soft-wraps to two downstream.
  Read `textAlignHorizontal` and the box sizing from Figma; don't infer them from position.
- **Not settling output scale up front** — discovering "it ships at three sizes" after baking
  single-scale PNGs means a re-import; ask it on the setup panel in the first five minutes.
- Binding a Figma link or local path directly — unfetchable; lift to a Picsart-hosted URL first
  (a `figma.com` SVG is CSP-blocked in preview just like any figma.com asset).
- Nagging about the Figma connector after the designer already chose the flat path.

Publisher release notes

Picsart - v2.1.0 Generate media, edit scenes, and plan films and motion design from conversation. Browse model options and parameter schemas, validate a request and inspect its best-effort credit estimate before generation. Image, video and audio work can return background job receipts. Progress checks retrieve completed results without submitting another job; failures remain visible. Text generation follows its supported model contract. Background removal, replacement, enhancement and vectorization create new media while preserving originals. Film Setup and the Shotlist Board collect the user's choices before prompts are compiled for generation. Asset Review supports approving character, location and prop references for a Drive asset library. Motion Setup establishes the format and movement direction; the Scene Editor supports reviewing and editing scenes in the browser. Decisions require actual widget feedback before the assistant continues. Compose layered scenes, animate titles, apply effects, looks and gradients, and import supported Figma paint or SVG path data. Built-in guides explain common authoring workflows. Validate a scene and export video or still images; completed exports offer accessible media links and downloads. Layout inspection and other capabilities depend on the tools exposed by the connected deployment; preview capabilities are excluded from this production submission. Transcribe speech, describe videos, plan reframing, or analyze audio where supported. Check the operation's current pricing and obtain approval before any priced analysis. An accepted job is followed to a terminal result before its findings are presented. Browse and organize Drive files, create folders, upload media, move files and send selected files to trash. Generated and edited media attempt Drive saving where supported; a save is reported only when confirmed. Results, model selection, progress, file intake and production boards provide interactive views in conversation.

Declared in the saved package. Remote tools may change independently.

Package details

Publisher declarations from the archived package. These are separate from our research and the live service's terms.

Package author
Picsart
Commerce declaration
Does not support commerceThis does not establish whether access is free or paid.
Publisher review scenarios
5 positive · 3 negativeDeclared scenarios, not independently verified test results.

Declared capabilities

  • Interactive
  • Read
  • Write

Package observed Oct 10, 2026.

Technical details
First seen
Sep 30, 2026 · 22:02 UTC
Last seen
Oct 10, 2026 · 18:00 UTC
Latest observed change
Oct 9, 2026 · 18:02 UTC
Collection status
Collected

plugin_asdk_app_6a18d4c190a0819186e6b129a09e931e

Download plugin data (JSON)

Before you connect Picsart

How do I connect it?

Open the publisher's marketplace listing to check current availability and follow its connection instructions. This directory does not install plugins. Check the requested access and any account requirements before connecting.

Check marketplace availability ↗

Does it require paid access?

We have not established the pricing or subscription requirements for this plugin. An absent price does not mean free access.

Compare researched pricing and access models →

How can I evaluate it?

Check the declared skills and available files, then try a small task whose result you can verify. Our archived descriptions and instructions establish publisher claims, not tested runtime quality. Review sources and coverage limits.