Visuals (Storyboarding)
This is one task, not a step in a workflow. Do it whenever the user requests it; you can come back to it after editing the clip.
Only two prerequisites: the ledger and subtitles must already exist (layers are placed by subtitle screen, timing is calculated from the ledger).
Needs edit-list.json, transcript.json, subtitles.json
Produces visuals.json + modules/<sequence-name>/index.html
Stop when done. Do not direct the user to the next step.
First, read and execute the Business Skill Phase Contract. For module writing strict rules, see the Module Contract. For judgment rules, see Visual Judgment.
0. Readiness
First, execute the "readiness check" from Check for Updates — whether skills are up to date and the Runtime is compatible; the plugin root is located there (in this document, <pluginRoot> should be substituted with that literal path). Only proceed if the result is "Ready". If the result is "New Session Required" or "Stop", execute its instructions (including the "no custom alternative interface" ban). The Business Skill does not contain its own environment logic.
If the readiness result is runtime.kind=desktop-managed, directly reuse the desktop App's installed stable CLI and the same launchd/windows-task service; do not resolve Electron paths, install additional dependencies, or start a second Runtime.
Commands
node "<pluginRoot>/scripts/ensure-running.cjs" --json
node "<pluginRoot>/scripts/videocut-cli.cjs" visual get <project> --json
node "<pluginRoot>/scripts/videocut-cli.cjs" visual frame <project> --cues sub-0004,sub-0005 --count 12 --out <dir> --json
node "<pluginRoot>/scripts/videocut-cli.cjs" visual add <project> --module modules/01-xx/index.html --cues sub-0004,sub-0005 [--zoom x,y,w,h] [--id vis-0001] --json
node "<pluginRoot>/scripts/videocut-cli.cjs" visual remove <project> --id vis-0001 --json
ensure-running must first prove that canonical 5190 belongs to the managed service of the current platform; if it fails, pass through the error and stop. Do not fallback to foreground or a custom port.
Layers are bound to subtitle cues (--cues); the product converts them to word IDs — a layer can never bind to words that no one speaks. If the edit changes, the layers move with it. Do not store seconds.
Four Steps, No Skipping
① Text defines task Read subtitles.json, segment by semantics, for each segment answer:
Tutorial/Demo/Proof, or Concept/Logic/Transition?
② Frame inspection `visual frame` to extract 8-12 frames, actually look at them.
In practice, 2 out of 12 segments had their text prediction overturned by visuals — this step is not a formality
③ Motion window Compute pixel difference between adjacent frames, find scrolling/switching/interaction moments.
Layers only cover stable windows; annotations exit before the motion starts
④ Build layer, place layer Write module → visual add → verify each layer in preview
Judgment rules for step ②
Visuals exactly match what is being said → Annotate: circle it, follow the word being spoken. Real evidence must not be covered.
Visuals weakly related / blank → Black animation: full-screen white background, styled from the style library.
Visuals are already clear by themselves → Do nothing. This is the most common correct answer (in practice, 4 out of 13 segments require no action).
Output from step ③ is numbers, not feelings
Circle coordinates Derive from pixel statistics (grayscale scan to find text lines), never by eye — eyeballing previously missed by 50px
Interaction moments Find sudden changes by per-frame pixel difference (one measured jump from 4 to 335), annotation finishes exiting 0.2s before the jump
Stable window Scrolling is often present at the beginning (user is searching for content); layer binds to the cue starting at the stable screen
Think before using zoom
Default: no zoom. 960x720 screen recordings become blurry when magnified, the user will notice immediately
If zoom is unavoidable ≤1.6x, region centered at 62.5% square — composition integrity > reading small text
Text too small to read Use annotations to guide the eye; if it must be readable, the answer is a high-resolution re-record, not magnification
Review Loop
Module directories are named with sequence numbers (01-daily-report, 05-anim-task-split). The timeline track shows the directory name; the user reports the number and you modify that layer, leaving others unchanged.
Two layer modification disciplines, each corresponding to a real incident:
When modifying a layer, always check the module After shortening/extending a layer, the hard-coded relative timings inside the module do not automatically update — the annotation's exit timing may fall after the layer ends, never appearing
Verify by checking pixels Take a screenshot to check the visual, do not inspect DOM properties. An iframe with "visibility toggle correct" was physically off screen — all properties correct, screen completely empty
What Not To Do
- Do not skip frame inspection or motion window analysis and go straight to production (both types of errors occurred once: covering real evidence, annotations floating on a scrolling scene)
- Do not make something for every segment — filling is not the goal; doing nothing is a common answer
- Do not store seconds, do not guess coordinates, do not add dark overlays on white backgrounds
- Do not fill with placeholder images when there is no real content
- Style must be selected from the registry, never invented
- Brand icons must use official glyphs, not hand-drawn substitutes (X was drawn with two intersecting lines, caught by the user). If not in the local index, fetch a single-color official glyph from simple-icons (black-and-white style works well), save the original SVG into the module directory for reference; the same brand must look identical within a single video