@wji.ai/cli 0.1.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,219 @@
1
+ # Canvas recipes for short drama
2
+
3
+ **On WJI** (`canvas_*` tools, no shell): the layouts below are the contract — which node sits in
4
+ which row, and in what order the references are connected. Read them as written and substitute the
5
+ tool calls from `operations-wji.md`; `--from A B C` is `fromNodeIds: ['A','B','C']` and the array
6
+ order is the reference order. Everything about rows, `@imageN` order and prompts is identical.
7
+
8
+ Command sequences for the three canvas types. All ids are placeholders — take real ones from the
9
+ command output (`wji --json canvas add … | jq -r .id`) and `bind` them at once. `SD` below is
10
+ `node {baseDir}/scripts/short-drama.mjs`.
11
+
12
+ Common setup (once per project):
13
+
14
+ ```bash
15
+ wji ws create "<title>" -r 9:16 --json | jq -r .id # default template: dataset, image generation, canvas → WS
16
+ wji ws use WS
17
+ SD bind <project> wji.workspaceId WS
18
+ DS_APP=$(wji --json ws get | jq -r '.apps[] | select(.app=="Dataset") | .id')
19
+ wji --json ws fs --app $DS_APP | jq -r '.. | objects | select(.content.semanticKey?) | "\(.content.semanticKey) \(.id)"'
20
+ SD bind <project> wji.datasetFolders.character <id> # story / character / location / prop / shot
21
+ # zsh: a variable holding several ids is not word-split — pass ids literally or pipe through `while read`
22
+ wji models list --feature image # pick the image model once (GPT Image 2.5 Sunburst if listed)
23
+ wji models get "<image model>" # confirm 1:1 and 16:9 ratios and the max reference count
24
+ ```
25
+
26
+ ## 1. `characters` canvas
27
+
28
+ ```bash
29
+ wji canvas create characters --json | jq -r .id # → CV; bind wji.canvases.characters
30
+ # row for C01 (rows 700 px apart: --at 0,0 / 0,700 / …)
31
+ wji canvas add title -c CV -t "C01 林小夏" --at 0,0 --json | jq -r .id # → T1; bind characters.C01.wji.nodes.title
32
+ wji canvas add image -c CV -n "C01 headshot" -m "<image model>" -r 1:1 \
33
+ --at 400,0 -p "<image.prompt>, clean front-facing portrait, plain neutral background" --json | jq -r .id # → H1
34
+ wji canvas add image -c CV -n "C01 sheet" --from H1 -m "<image model>" -r 16:9 \
35
+ --prompt-file /tmp/C01-sheet.txt --json | jq -r .id # → S1 (file = "@image1 is the face to match exactly. " + image.sheet)
36
+ wji canvas add audio -c CV -n "C01 voice" --from H1 -m seed-audio-1.0 \
37
+ -p "$(SD prompt <project> voice C01)" --json | jq -r .id # → V1; references the HEADSHOT
38
+ # the prompt is (age;identity;personality;timbre;pitch;pace;accent;emotion;此刻<direction>)<line>
39
+ SD bind <project> characters.C01.wji.nodes.headshot H1 # sheet S1, voice V1
40
+ ```
41
+
42
+ Generate: headshots for all characters are one batch (quote the total, or `-auto`), then look at
43
+ each (`wji canvas get H1 --json | jq -r .assetId` → `wji asset download <asset> -o /tmp/sd`), then
44
+ sheets. `wji canvas gen S1 --yes --wait` per node; the result's asset id →
45
+ `bind characters.C01.wji.assets.sheet`. Attach to the dataset:
46
+
47
+ ```bash
48
+ wji fs set <C01 dataset> --add-images <headshot> <sheet> <voice> --icon <headshot> --set primaryAudio=<voice>
49
+ ```
50
+
51
+ Reference for the sheet prompt file: `@image1 is the face to match exactly — same features, same
52
+ hairstyle. ` + `image.sheet`.
53
+
54
+ ## 2. `scenes` canvas — one row per scene
55
+
56
+ ```bash
57
+ wji canvas create scenes --json | jq -r .id # → SC; bind wji.canvases.scenes
58
+ wji canvas add title -c SC -t "S01 便利店店内" --at 0,0
59
+ wji canvas add image -c SC -n "S01 sheet" -m "<image model>" -r 16:9 --at 400,0 --prompt-file /tmp/S01-sheet.txt # → SS1
60
+ wji canvas add image -c SC -n "S01 lighting 监控回放绿" --from SS1 -m "<image model>" -r 16:9 \
61
+ -p "@image1 is this exact space; keep structure, materials and wear identical; change only the lighting: <lighting prompt>. Empty scene, no people, cinematic film still"
62
+ ```
63
+
64
+ Variants: `--from` the parent scene's sheet node, prompt = parent master view + `changes` +
65
+ `keep the structure, materials and wear identical to @image1`.
66
+
67
+ After generation: inspect (no people, one space across panels), attach
68
+ (`--add-images <sheet> <variants…> --icon <sheet>`), bind `scenes.S01.wji.nodes.sheet`,
69
+ `scenes.S01.wji.assets.sheet`, `scenes.S01.wji.assets.lighting.<state>`.
70
+
71
+ ## 2b. `props` canvas — one row per prop
72
+
73
+ Props live on their own canvas: white background, `handheld scale` / `tabletop scale` /
74
+ `furniture scale` in the prompt, one **state** node per `states[]` entry referencing the sheet.
75
+
76
+ ```bash
77
+ wji canvas create props --json | jq -r .id # → PC; bind wji.canvases.props
78
+ wji canvas add title -c PC -t "P01 旧监控硬盘" --at 0,0
79
+ wji canvas add image -c PC -n "P01 sheet" -m "<image model>" -r 16:9 --at 400,0 --prompt-file /tmp/P01-sheet.txt # → PS1
80
+ wji canvas add image -c PC -n "P01 state 接在主机上" --from PS1 -m "<image model>" -r 16:9 \
81
+ -p "@image1 is the prop to reproduce exactly. <state prompt>, pure white background, no people, no hands"
82
+ ```
83
+
84
+ Inspect (no hands, no people, the same object in every panel), attach
85
+ (`--add-images <sheet> <states…> --icon <sheet>`), bind `props.P01.wji.nodes.sheet`,
86
+ `props.P01.wji.assets.sheet`, `props.P01.wji.assets.states.<state>`. On the episode canvas the
87
+ prop's sheet becomes a display node like any other (`wji.displayNodes["P01:sheet"]`).
88
+
89
+ ## 3. `E01` canvas
90
+
91
+ ```bash
92
+ wji canvas create E01 --json | jq -r .id # → E1; bind --ep 1 wji.canvasId
93
+ # row 0 — the sheets this episode uses, as display nodes (assets already generated in stages 2–3)
94
+ wji canvas add image -c E1 -n "C01 sheet" --asset <C01 sheet asset> --at 0,0 --json | jq -r .id # → D_C01
95
+ SD bind <project> --ep 1 wji.displayNodes.C01:sheet D_C01
96
+ wji canvas add image -c E1 -n "S02 sheet" --asset <S02 sheet asset> --at 500,0 … # D_S02, D_C03, D_P01 …
97
+ wji canvas add audio -c E1 -n "C01 voice" --asset <C01 voice asset> --at 2000,0 # D_C01v (only if a voice exists)
98
+ wji canvas add dataset -c E1 --dataset <C01 dataset id> --at 0,300 # optional: the character card, for people reading the canvas
99
+
100
+ # row 1 — segment E01-01 (default: reference images → video, no storyboard images)
101
+ SD prompt <project> segment E01-01 > /tmp/E01-01-prompt.txt # the header line lists refs: in the order to connect them
102
+ wji canvas add title -c E1 -t "E01-01 · 雨棚 · 15 s" --at 0,700
103
+ wji canvas add video -c E1 -n "E01-01" --from D_S02 D_C01 D_C03 D_P01 D_C03v D_C01v -m seedance-2.5-ref -r 9:16 \
104
+ --resolution 480p --prompt-file /tmp/E01-01-prompt.txt --json | jq -r .id # → VID1
105
+ # D_S02 D_C01 D_C03 D_P01 → @image1 … @image4 · D_C03v D_C01v → @audio1 @audio2 (pictures, then clips, then voices)
106
+ SD bind <project> --ep 1 storyboard.segments.E01-01.wji.nodes.video VID1
107
+ wji canvas add text -c E1 -n "E01-01 prompt" --text-file /tmp/E01-01-prompt.txt
108
+ wji canvas gen VID1 # prints the cost and refuses → tell the user → then --yes --wait (or -auto)
109
+ ```
110
+
111
+ `wji canvas get VID1` must print `@image1 ← D_S02`, `@image2 ← D_C01` … exactly as the prompt's
112
+ legend says; fix with disconnect/connect if it does not. With `videoDuration: auto` (the default)
113
+ there is no `-d`; `minimax-h3-ref` has no auto duration, so h3 projects pass `-d` = Σ cut seconds.
114
+
115
+ **Only with `params.references: keyframes`** — one generated image per cut, referenced by the video
116
+ node instead of the sheets (an opt-in for shots that must be composed exactly; twice the images to
117
+ pay for and approve):
118
+
119
+ ```bash
120
+ wji canvas add image -c E1 -n "E01-01 f1" --from D_S02 D_C01 D_C03 D_P01 -m "<image model>" -r 9:16 --at 400,700 \
121
+ --prompt-file /tmp/E01-01-f1.txt # → K1 … K5 (prompt = plan prefix + cut.frame)
122
+ SD bind <project> --ep 1 storyboard.segments.E01-01.wji.nodes.keyframes.0 K1
123
+ wji canvas gen K1 --yes --wait # first segment: generate, download, inspect, fix, then the rest
124
+ wji canvas add video -c E1 -n "E01-01" --from K1 K2 K3 K4 K5 D_C03v D_C01v -m minimax-h3-ref -r 9:16 -d 15 \
125
+ --resolution 768P --prompt-file /tmp/E01-01-prompt.txt --json | jq -r .id # → VID1
126
+ ```
127
+
128
+ Video models, one per `params.video`: `h3` → `minimax-h3-ref`, `seedance` → `seedance-2.5-ref`,
129
+ `seedance2` → `seedance-2.0-ref`, `seedance2fast` → `seedance-2.0-fast-ref`, `wan` → `wan-3.0-ref`.
130
+ `prompt segment` prints the right one under `video.model` with the resolution already in that
131
+ model's spelling — copy both rather than typing them, and `wji models get <name>` confirms the caps.
132
+ Pass `-d` **only in `videoDuration: fixed`** (it switches `autoDuration` off, which is what pins the
133
+ clip to the storyboard); in `auto`, leave it out. `minimax-h3-ref` has no `autoDuration`, so it is
134
+ always `-d`.
135
+
136
+ **Connect the voices too.** Everyone who speaks in the segment brings their voice asset as an audio
137
+ reference — `canvas add audio --asset <voiceAsset>` once per character on the episode canvas, then
138
+ `--from` them **after** the pictures (and after the clip reference, if there is one: `canvas connect`
139
+ labels each edge as it is made, and `wji canvas connect <prev video node> <this video node>` prints
140
+ `as @video1`, which is how to check the numbering landed where the prompt expects). They become `@audio1…M` (their own series, so a voice
141
+ connected after three sheets is `@audio1`, not `@image4`), and the prompt attributes every one of
142
+ that character's lines to the label. Caps: 3 audio references on `minimax-h3-ref` and the Seedance
143
+ 2.0 pair, 5 on `wan-3.0-ref`, 10 on `seedance-2.5-ref`. `prompt segment` prints the whole list,
144
+ pictures then voices, in the order to connect them; drop them only if the model shows no audio
145
+ reference support (`params.voiceSamples: false`).
146
+
147
+ The segment's **soundtrack node** (Seed Audio; `params.audioTrack`, default `bed`) sits beside the
148
+ video node — no `--from` for a bed, the voice display nodes in speaking order for `--full`:
149
+
150
+ ```bash
151
+ SD prompt <project> audio E01-01 > /tmp/E01-01-audio.txt # timecoded ambience / sfx / music
152
+ wji canvas add audio -c E1 -n "E01-01 track" -m seed-audio-1.0 --prompt-file /tmp/E01-01-audio.txt --json | jq -r .id # → A1
153
+ SD bind <project> --ep 1 storyboard.segments.E01-01.wji.nodes.track A1
154
+ wji canvas gen A1 --yes --wait # ~0.15 credits a minute; attach: fs set <shot dataset> --add-images <asset>
155
+ # --full: wji canvas add audio … --from D_C02_VOICE D_C01_VOICE (→ @Audio1 @Audio2, the order the prompt names them)
156
+ ```
157
+
158
+ `wan` mode, one clip per segment (the model speaks the dialogue itself, so no voice nodes):
159
+
160
+ ```bash
161
+ wji canvas add video -c E1 -n "E01-01" --from K1 K2 K3 -m wan-3.0-ref -r 9:16 -d 15 \
162
+ --resolution 480P --prompt-file /tmp/E01-01-prompt.txt
163
+ ```
164
+
165
+ `wan-3.0-ref` takes at most **10** reference images and whole-second durations (2–30 s); the
166
+ `timing` gate already refuses a segment that breaks either rule. `--set audio=false` turns the
167
+ generated sound off if you are laying your own track.
168
+
169
+ `wan-3.0-ref` rejections from DashScope's green-net (`DataInspectionFailed`) are random and free —
170
+ retry the same node 8–12 times, then shorten the prompt (see `stage-storyboard.md`).
171
+
172
+ Queue: FREE accounts run 3 jobs at once — submit 3 keyframes, `wji history get <id> --wait` (or
173
+ `wji canvas get <node>` until `done`), submit the next 3. A 429 was rejected before charging;
174
+ wait, don't loop.
175
+
176
+ After the clip: `wji fs set <E01-01 shot dataset> --add-images K1a K2a … --icon K1a --set primaryVideo=<clip asset>`,
177
+ bind `storyboard.segments.E01-01.wji.assets.video`, then `SD manifest <project> --ep 1`.
178
+
179
+ ## Reference order after `connect`
180
+
181
+ WJI numbers connected references **per type and in edge order** — `@image1 @image2 …` for pictures,
182
+ `@video1 …` for clips, `@audio1 …` for the character voices — and that numbering is what the prompt
183
+ addresses. So a voice node connected alongside three sheets is `@audio1`, not `@image4`.
184
+
185
+ References are numbered by edge creation order. `canvas connect` appends, so adding a sheet later
186
+ puts it *last* even if the legend says `@image2`. To restore the legend order, `disconnect` the
187
+ edges that should come after it and `connect` them again in order — then `canvas get <video>`
188
+ must print `@image1 ← <scene>`, `@image2 ← <person>` … exactly as `prompt segment` lists them.
189
+
190
+ ## Waiting for long clips
191
+
192
+ `canvas gen --wait` gives up after 600 s by default and a 25–30 s Seedance clip can take longer;
193
+ the task keeps running. Pass `--timeout 900`, or pick it up with `wji history get <id> --wait` —
194
+ never resubmit a node whose status is still `generating`.
195
+
196
+ ## Measured prices (2026-09, one FREE account — re-check with `canvas gen` without `--yes`)
197
+
198
+ With `params.videoReferences: previous`, the previous segment's **video node** is `--from`'d
199
+ directly — it already carries the clip, so no display node is needed, and segment k cannot be built
200
+ until k−1 has generated.
201
+
202
+ | Generation | Credits |
203
+ | --- | --- |
204
+ | gpt-image-2.5-sunburst 1:1 1k (headshot, keyframe) | 18.3 |
205
+ | gpt-image-2.5-sunburst 16:9 1080p (sheet) | 14.34 |
206
+ | wan-3.0-ref 480P | 15 / s (15 s = 225) |
207
+ | minimax-h3-ref 480P | 15 / s (15 s = 225) |
208
+ | seedance-2.5-ref 480p, auto duration | ≈ 31 / s, billed on the real length (≈30 s = 900–930) |
209
+
210
+ The Seedance 2.0 pair was not price-measured here; `canvas gen` without `--yes` quotes before
211
+ charging, and `seedance-2.0-fast-ref` is the cheap one — use it for a draft pass of a shot you
212
+ expect to re-cut, then re-run the approved segments on 2.5.
213
+
214
+ A rejected submission (content filter, real-person check, 429) is not charged.
215
+
216
+ ## Cleaning up
217
+
218
+ `wji canvas rm` and `wji fs rm` are destructive — name the targets in the chat and wait for a yes
219
+ (the `wji` skill rules). Never delete a node whose asset is attached to a dataset without saying so.
@@ -0,0 +1,116 @@
1
+ # Motion craft — what happens over the seconds, and how the prompt says it
2
+
3
+ The method behind `motion`, `cameraNote`, `sound` and the prompt-hygiene rules, distilled from
4
+ the user's `short-drama-video-prompts` skill. The frame says how a cut starts; **the motion
5
+ sentence says what changes between the start and the end boundary**, and the prompt is delivery
6
+ text for a machine, not prose for a reader.
7
+
8
+ ## 1. The seven parts of a motion (gates `motion`, `sound`)
9
+
10
+ 1. **Start anchor** — the start boundary, in the present tense, static: "her hands are crossed on
11
+ her knees, her eyes on the puddle". Never "she is about to stand" (that is already motion).
12
+ 2. **Ordered subject motion** — `motion.trigger` (the cue: a sound, a line, a look), `motion.action`
13
+ (one visible state chain: who, what, direction, contact, landing — a hand-over names the giver,
14
+ the receiver, the hand and where the object lands) and `motion.result` (the end state, or
15
+ `hold`). Causal order, not a list: "hearing the latch, her eyes go to the door first; her left
16
+ hand stops on the page; only then does her back leave the chair". "Slowly, naturally,
17
+ cinematic" are not paths.
18
+ 3. **Performance arc** — never emotion labels in sequence (calm → tense → shocked). Trigger →
19
+ what is received → one or two visible signals in a channel the size can read (gaze, breath,
20
+ body set, object handling) → the decision (an action taken or withheld) → the landing at the
21
+ end boundary. A wide cannot carry an eyelid; a close-up cannot carry a full-body retreat.
22
+ Keeping still is a valid performance when it is the character's strategy.
23
+ 4. **Camera** — `cameraNote`. A lock-off is written like a move: the framing, **what still moves
24
+ inside it**, how long it holds (`hold`). An empty camera line is read as "free to drift", and
25
+ the model grows a push-in nobody asked for. A move names its `motivation` (reveal / pressure
26
+ / alignment / relationship / transition / stillness), its `trigger`, its `path` and tempo, and
27
+ its `endFraming` inside the cut's boundaries. Lock and move never rule the same interval
28
+ without a stated transition. One camera idea per cut; camera words are dialect-specific
29
+ (`CAMERAS` is H3's list, `CAMERAS_ZH` wan's) — do not carry a term validated on one model to
30
+ another.
31
+ 5. **Environment** — only motion that touches the subject or carries a cue: wind on a curtain,
32
+ steam, a reflection. Nothing invented for liveliness; weather or damage that is not in the
33
+ accepted state does not appear.
34
+ 6. **Sound** — `segment.sound`: `ambience` (the room tone, **identical across every segment of a
35
+ scene** — separately generated clips each invent their own, and every cut point then "whooshes"
36
+ into a new room), `sfx` pinned to a cut, `music` as `N/A` or an intent (where it enters, what it
37
+ sits under — music is implemented on the timeline, not burned into a clip). For a same-pass
38
+ model the sound section is a **complete timeline**: every voice event in order with speaker and
39
+ spoken language, ambience or silence between them, closed with the exclusions the script
40
+ declared. Left undefined, the seconds fill with sound the model chose.
41
+ 7. **End report** — the `end` boundary, field by field. A "more natural ending" in the prompt does
42
+ not override it; if the motion cannot reach it, the storyboard changes, not the boundary.
43
+
44
+ ## 2. Timing is arithmetic (gate `timing`)
45
+
46
+ The cut seconds are the explicit timing plan and they sum to the segment. Both directions are
47
+ defects: **overflow** is truncated — the closing action, the line's tail and the end pose go with
48
+ it; **shortfall** is not a held frame — the model fills the unallocated seconds with motion that
49
+ has no source. A line needs a lead-in before the mouth opens (`speechLeadSeconds`, 0.5) and a
50
+ landing after it; a 4.0 s line does not fit a 4 s cut. Measured on H3 Chinese: ~4.1 speakable
51
+ characters a second, with rushing first and silent truncation after — lengthen the cut, split
52
+ it, or ask the script to cut the line; never quicken the delivery to make the sum work.
53
+
54
+ ## 3. Delivery text carries only what is to be filmed (gate `prompt-hygiene`)
55
+
56
+ Inside the prompt there is no room for how the work was done:
57
+
58
+ | never in the prompt | why |
59
+ | --- | --- |
60
+ | file names, versions, paths (`sheet_v3.png`) | rendered as text on screen |
61
+ | entity ids, segment ids, rule ids, status words | the same, or read as an instruction to draw them |
62
+ | sketch references ("as in panel 2"), retake notes ("fix the hands this time") | meaningless to the model, and "hands" becomes a subject |
63
+ | walls of negatives ("no extra people, no deformed hands, no text…") | the nouns become the picture; write the positive fact instead ("only the two of them in frame, both hands complete and visible") |
64
+ | `…` left from the skeleton | an unfilled slot |
65
+
66
+ Bind on **every** mention or on none. In sheets mode a person is `@imageK` in every shot they
67
+ are in; bound once and then "she" is read as two different people. The skeleton does this for the
68
+ lines; the gate checks it per shot.
69
+
70
+ **Subtitles are off unless asked.** Same-pass models add captions on their own; every prompt
71
+ says so in its own language (`no subtitles, no captions, no on-screen dialogue text` / `全程无字幕`)
72
+ — `params.subtitles: true` to allow them. Readable on-screen text (a prop with `textPolicy:
73
+ readable`) is exact, everywhere it is shown, and never cancelled by a global "no text".
74
+
75
+ ## 4. Per model, measured
76
+
77
+ **MiniMax H3** (`minimax-h3-ref`). Structured fields in English, dialogue kept in the original
78
+ language inside `<d>[Chinese] …</d>` — only the line, no parenthetical, no seconds. On first
79
+ mention a speaker is placed on- or off-screen with a timbre; **when another face is on screen
80
+ during a line, write that they `do not speak; lips remain completely closed`** — 3/3 correct
81
+ lip-sync against 2/3 without it, because the model puts the mouth on the most visible face; better
82
+ still, have the non-speaker turned away or out of frame. `non_diegetic_music: N/A` when there is
83
+ no score (omitting it produced music). Cut timestamps land near but not exactly; `<scenetrans>`
84
+ carries a line across an in-generation cut, `<cutoff>` ends a clip mid-line only when the script
85
+ wants it. Duration is any integer 4–15 (WJI's form: 5–15); first/last-frame inputs and reference
86
+ inputs are mutually exclusive, so a start frame plus sheets all go in as references.
87
+
88
+ **Seedance 2.5 / 2.0** (`seedance-*-ref`). `镜头 k [0:00–0:03]` timelines with no gaps and the
89
+ last endpoint equal to the total; bind on every mention; 2.5 takes `extend` (continue from an
90
+ actual clip plus its actual tail frame as `@image1`) and `edit`; tail frames improve the last
91
+ frame's landing but hand the path between two frames to interpolation — an action the cut exists
92
+ for must not rest on that gap. Two-frame chaining is not available on WJI today; `videoReferences:
93
+ previous` passes the previous clip only.
94
+
95
+ **Seed Audio 1.0** (`seed-audio-1.0`) — the soundtrack node. One prompt is a mini script:
96
+ name the environment, each element and how the scene unfolds; `[start s:end s]` in front of an
97
+ element pins it to that interval (100 ms precision), which is how the bed lines up with the cuts
98
+ and how music is made to duck, swell or fall silent at a moment ("Let the music drop before the
99
+ key line" is the documented idiom). Up to 120 s per generation, 2048 characters, three reference
100
+ voices as `@Audio1..3` **or** one reference image, never both; a reference clip is under 30 s.
101
+ Ask for `instrumental` when there are to be no vocals, and say "no dialogue" on a bed — left
102
+ open, it invents a narrator. `prompt audio` derives all of this from `segment.sound`.
103
+
104
+ **Wan 3.0** (`wan-3.0-ref`). Chinese prompt, same-pass audio, the 音频 line is the whole sound
105
+ design; the content filter judges the assembled prompt's *length* (`stage-storyboard.md`), so the
106
+ motion and camera clauses stay terse.
107
+
108
+ ## 5. Self-check before generating
109
+
110
+ Does the prompt start from the exact start boundary and restate only what this execution needs?
111
+ Is every person and prop that acts, reacts or is handed over resolvable — bound or described? Do
112
+ two people on screen resolve to two different bindings on every mention? Has every performance
113
+ change a trigger and a visible channel? Do the seconds add up in both directions, with the
114
+ lead-in? Does the sound section cover the whole cut — lines, ambience between, music declared?
115
+ Is there any file name, id, note or negative dump left? Would the end the prompt describes match
116
+ the `end` boundary field by field?
@@ -0,0 +1,177 @@
1
+ # Shot craft — how a cut earns its place
2
+
3
+ The method behind the storyboard fields added to stage 5, distilled from the user's
4
+ `short-drama-storyboard` skill. It applies in **both** reference modes: in `keyframes` the frame
5
+ becomes an image, in `sheets` it is the shot's description inside the segment prompt — either
6
+ way the cut carries the same shot grammar, and `prompt segment` composes it. What can be checked
7
+ is a gate, named per section; the rest is judgement.
8
+
9
+ ## 1. A cut has one job — `purpose` (gate `shot-purpose`)
10
+
11
+ A cut exists to change what the audience knows, who holds power, the pressure in the room, the
12
+ space, or the rhythm. "Medium shot of her talking" is not a purpose. Design in this order:
13
+
14
+ 1. find the key action in the beats the cut claims — an act, a line, a discovery, a refusal, a
15
+ hand-over, a reveal;
16
+ 2. say what changes for the audience or a character because of it (`purpose.change`);
17
+ 3. decide whose experience the picture is organised from;
18
+ 4. fix what must be visible at the start and the end;
19
+ 5. only then choose the size, the camera move and the cut point.
20
+
21
+ `purpose.kind`: `establish` (space, who is where), `reveal` (new information enters), `evidence`
22
+ (the object the audience must read), `reaction` (the receiver's face or hands), `pressure`
23
+ (distance closes, an exit is taken), `decision` (a visible choice), `consequence` (the result
24
+ lands), `performance` (the line and the body that says it), `transition`.
25
+
26
+ **Cut on a change.** Adjacent cuts differ in at least one of size, angle or who is on screen —
27
+ the same framing twice is a jump cut with a number. **A turn needs a reader.** A cut claiming a
28
+ script `turn` beat or the 爽点 is a reveal-class cut (`reveal / evidence / decision /
29
+ consequence`), or the next cut is a `reaction` on someone who did not speak the line — a blow
30
+ nobody is seen to receive did not land. **The cliff cut** (the episode's last beat) is `reveal /
31
+ consequence / reaction / pressure`; it never resolves.
32
+
33
+ ## 2. The size ladder
34
+
35
+ | 景别 | what it carries |
36
+ | --- | --- |
37
+ | 远景 `extreme-wide` | geography, the person swallowed by the space; no expression |
38
+ | 全景 `wide` | the room, who controls the door, group power |
39
+ | 中景 `medium` | interaction, hands, two-person strategy, blocking |
40
+ | 近景 `close` | a decision, evidence, a hidden reaction |
41
+ | 特写 `extreme-close` | one face, one hand, one wound — the only fact in frame; the space is gone |
42
+
43
+ A scene opens one step wider than it will play, lands on its **working size** on the second
44
+ cut, then steps a size up or down around it; a wide or an extreme close-up is an excursion,
45
+ used once and returned from. Do not use the size as an emotion dial — the question is always
46
+ *where does the information the audience must read live*: on the knuckles, give the extreme
47
+ close-up; in the distance between two people, do not cut in. A segment that only tightens has
48
+ nowhere to go by its middle (`storyboard-advice`).
49
+
50
+ **9:16 narrows the ladder.** Wides carry information sideways, and a phone has a third of the
51
+ width. Vertical drama plays in medium / close / extreme-close, and space is built in **depth**:
52
+ foreground, mid-ground, background — a doorframe, a counter edge, a shoulder in front, the room
53
+ behind (`foreground`, `background`). A wide on 9:16 must say why (`note`) or carry a `background`
54
+ that does the geography.
55
+
56
+ ## 3. Three axes, three fields (gate `framing-axis`)
57
+
58
+ "A motivated angle" hides three separate decisions, and the common accident is writing 俯拍 when
59
+ what was meant was "from behind her shoulder".
60
+
61
+ | field | vocabulary | what it answers |
62
+ | --- | --- | --- |
63
+ | `angle` | `front` 正面 · `three-quarter` 斜侧 · `profile` 正侧 · `rear-three-quarter` 后斜侧 · `back` 背面 | how much face the audience may read, and whose side they stand on |
64
+ | `height` | `ground` · `low` 仰 · `eye` 平视 · `high` 俯 · `top` | who is bigger than whom this instant — `eye` is a *choice* for equals |
65
+ | `focal` | the lens (`FOCAL_BY_SIZE` or your own) | how far apart two people look |
66
+
67
+ They do not derive from each other: a low angle from behind is "we follow him as he grows", a
68
+ low angle from the front is "he is coming down on us"; a long lens on a wide flattens two people
69
+ across a room into one. Height is expensive on 9:16 — a tilt eats the vertical room the person
70
+ has — so carry power with angle and distance first.
71
+
72
+ ## 4. The axis — `segment.axis`, `axisSide`, `facing`
73
+
74
+ The 轴线 is the line between two named people. Write it as three checkable assertions, not an
75
+ adjective:
76
+
77
+ ```json
78
+ "axis": { "between": ["C01", "C02"], "workingSide": "the customer side of the counter, camera looking at the clerk over the oden warmer",
79
+ "screenLeft": "C02", "screenRight": "C01" }
80
+ ```
81
+
82
+ - **Bare left / right always means screen left / right.** A person's own side carries the
83
+ subject: "her right hand", "his left shoulder". Mixing the two readings is the usual cause of a
84
+ reversed reverse-shot, and every sentence looks fine on its own.
85
+ - **Reverse shots are complementary**: if A faces `screen-right` in her single, B faces
86
+ `screen-left` in his. Two singles facing the same way read as both looking at a third person.
87
+ Match the eyeline height to the real height difference; an asymmetric pair (one higher, one
88
+ tighter) is a statement of power — make it on purpose.
89
+ - **Over-the-shoulder** names whose shoulder is in the foreground (`foreground`); that decides
90
+ whose shot it is.
91
+ - **Crossing the axis** is legal with a neutral shot, an in-shot crossing, a deliberate
92
+ disorientation or a re-established geography — `axisSide: cross` with a `note` that says which;
93
+ the new working side is then inherited by the cuts after it.
94
+ - Three or more people: draw the **directed relationship** first (who protects / presses /
95
+ watches whom, from which beat), then place bodies, facing, distance and lines of action; one
96
+ representative reaction beats a row of identical gasps; background people do not change sides
97
+ between cuts without a reason.
98
+
99
+ ## 5. Start → one action → end (gate `continuity`)
100
+
101
+ Every cut has two boundaries, and the boundaries are the continuity system:
102
+
103
+ ```json
104
+ "start": { "positions": { "C01": "behind the counter" }, "facing": { "C01": "screen-left" }, "gaze": { "C01": "at the bag" },
105
+ "hands": { "C01": "both hands on the counter edge" }, "heldProps": { "C01": [] }, "state": ["the bag lying on the counter"] },
106
+ "end": { … the same fields … }
107
+ ```
108
+
109
+ - `start` is written on a segment's **first** cut only; every later cut begins where the previous
110
+ one ended (the script derives it), and a segment in the same scene begins where the previous
111
+ segment ended — positions and held props are compared.
112
+ - Each boundary is an absolute fact readable without the last cut ("on the stool" — never "same
113
+ as before"). A person who enters on a cut has no start; `render segment` marks them 入画.
114
+ - **A prop changes hands only on an action beat.** A hand-over between two lines is teleporting.
115
+ - `state[]` are short visible facts ("the drawer slightly ajar", "steam rising off the bowl").
116
+ A state that only exists in `end` may not be named in `frame` — that is **keyframe drift**,
117
+ the picture already showing the result.
118
+
119
+ ## 6. The frozen frame — `frame`
120
+
121
+ The frame is the **start instant**, in both modes: the still a photographer could take at
122
+ second 0.00 of the cut. Twelve things make it renderable without the artist inventing any of
123
+ them; ~400–500 characters is full, 800 is padding:
124
+
125
+ | write | or the model decides it for you |
126
+ | --- | --- |
127
+ | where each person is and how far apart | spacing drifts cut to cut, the room seems to change |
128
+ | foreground / mid-ground / background | depth collapses into one layer |
129
+ | where the eye should land (the shot's one fact) | the frame is evenly weighted, nobody knows what to look at |
130
+ | composition and size phrase | each cut composed at random; they do not cut together |
131
+ | which side of the axis, height, lens | the reverse angle does not match |
132
+ | where the light comes from and its quality — its *result* on the face | the key jumps between cuts of one scene |
133
+ | every present person's pose, facing, hands, held props | the next cut cannot pick them up |
134
+ | what is the same as the previous cut, in one clause | "unwritten" is read as "free to change" |
135
+
136
+ **Static test**: no ordered verbs (then / before / after that / begins to / 先 / 再 / 然后),
137
+ no expression *changing*, no camera move, no weather *transforming* — those belong to `motion`.
138
+ A held pose can imply tension; it cannot require two moments. **No names**, no ids, no file
139
+ names: the frame is delivery text.
140
+
141
+ Lighting: `light.direction` is relative to the camera; `light.note` describes what the light
142
+ *does* on the face or the object ("the cabinet glow holds the right of his face; nothing moves
143
+ but the light on his jaw") — the result reads more stably than the position alone, and it must
144
+ say whether left / right is the screen's or the subject's.
145
+
146
+ ## 7. Generability (gates `filmable`, `motion`; advice in `storyboard-advice`)
147
+
148
+ A video model performs what it has seen a million times: walk in, sit, stand, turn, nod, hand
149
+ over, take, hold, push a door, set something down, reach. Four risk classes it does not perform:
150
+
151
+ | risk | example | rewrite |
152
+ | --- | --- | --- |
153
+ | precise interception | blocking a closing lid with a palm | close it, then keep the hand on it (two common actions + a hold) |
154
+ | invisible inner state | 像是听见了什么 · seems to realise | the visible action that shows it |
155
+ | the negative action | 没有接话 · does not press it | the action that happens instead — a hold, a hand that stays |
156
+ | a hand chain | four steps of two-hand work in four seconds | hand the change to an object, split the cut, or end on the result |
157
+
158
+ Drama lives in the **combination and timing** of ordinary actions, not in their precision. The
159
+ test: read the action to someone who has not seen the script — can they mime it at once?
160
+
161
+ ## 8. Key scenes — `storyboard.visualPlans[]` (gate `visual-plan`)
162
+
163
+ Only for scenes where the directing choice changes the experience: a first appearance, a reveal
164
+ of identity or evidence, a reversal, the climax or the cliff. `seed storyboard` writes the
165
+ worksheet for the 爽点 scene and the cliff scene; before cutting them answer 戏剧转向 (`turn`),
166
+ 观众立场 (`alignment`: where we stand at the start, where we are moved to), 空间压力
167
+ (`pressure`: entrances, distance, blocking, exits), 视觉推进 (`progression`: what lands first —
168
+ information, reaction or space), 摄影节奏 (`rhythm`: what triggers a lock, a move, a cut-in, a
169
+ hold), 反应落点 (`landing`: whose choice ends the scene) and 声音策略 (`sound`). Ordinary scenes
170
+ go straight to cuts.
171
+
172
+ ## 9. Review questions
173
+
174
+ Which beats does this cut realise, omit or repeat? What has changed when it ends? Why this size
175
+ and this camera behaviour? Can every person stand, see and move where the space puts them? Is the
176
+ end a start the next cut can pick up? If this cut were deleted or merged, what story meaning would
177
+ be lost — and if the answer is nothing, merge it.