@officexapp/vidfarm-devcli 0.21.34 → 0.21.36

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.
Files changed (28) hide show
  1. package/.agents/skills/editor-capabilities/SKILL.md +14 -3
  2. package/.agents/skills/vidfarm/SKILL.md +66 -33
  3. package/.agents/skills/vidfarm/harnesses/README.md +112 -0
  4. package/.agents/skills/vidfarm/{regimes/explainer.QA_REGIME.md → harnesses/explainer.HARNESS.md} +3 -2
  5. package/.agents/skills/vidfarm/{regimes/hooks.QA_REGIME.md → harnesses/hooks.HARNESS.md} +3 -3
  6. package/.agents/skills/vidfarm/{regimes/product-demo.QA_REGIME.md → harnesses/product-demo.HARNESS.md} +1 -1
  7. package/.agents/skills/vidfarm/{regimes/short-form.QA_REGIME.md → harnesses/short-form.HARNESS.md} +39 -10
  8. package/.agents/skills/vidfarm/{regimes/ugc-testimonial.QA_REGIME.md → harnesses/ugc-testimonial.HARNESS.md} +3 -3
  9. package/.agents/skills/vidfarm/recipes/{bulk-scripting-with-a-regime.md → bulk-scripting-with-a-harness.md} +20 -12
  10. package/.agents/skills/vidfarm/recipes/cutout-graphics-for-explainers.md +43 -13
  11. package/.agents/skills/vidfarm/recipes/local-edit-render-approve.md +1 -1
  12. package/.agents/skills/vidfarm/references/automation-and-local-dev.md +77 -26
  13. package/.agents/skills/vidfarm/references/editor-workflows.md +18 -5
  14. package/.agents/skills/vidfarm/references/hooks-and-virality.md +65 -7
  15. package/.agents/skills/vidfarm/references/reviewing-renders.md +2 -1
  16. package/.agents/skills/vidfarm-media/SKILL.md +2 -2
  17. package/.agents/skills/vidfarm-media/references/tts.md +26 -4
  18. package/SKILL.director.md +292 -98
  19. package/SKILL.md +33 -15
  20. package/dist/src/cli.js +1200 -141
  21. package/dist/src/devcli/handoff.js +54 -33
  22. package/dist/src/devcli/{qa-regime.js → harness.js} +132 -55
  23. package/dist/src/devcli/plate-key.js +698 -0
  24. package/dist/src/devcli/qa-check.js +209 -4
  25. package/dist/src/devcli/skill-docs.js +136 -0
  26. package/dist/src/devcli/sticker-pack.js +48 -0
  27. package/package.json +6 -4
  28. package/.agents/skills/vidfarm/regimes/README.md +0 -79
package/SKILL.director.md CHANGED
@@ -35,6 +35,7 @@ vidfarm serve template_<32hex> # local server + browser, opens that
35
35
  - The API key comes from https://vidfarm.cc/settings and starts with `vf_key_`. Instead of `login`, setting the `VIDFARM_API_KEY` environment variable also works for every command — the CLI reads it from the environment or from a `.env` file in the current directory.
36
36
  - No account or key? `vidfarm serve --no-cloud` still gives a fully local editor with free local renders.
37
37
  - "Open/run template X locally" is exactly one command: `vidfarm serve <template_id>` (alias: `vidfarm <template_id>`). Do not hand-roll REST or hunt for local `.harness/` files first — `serve` and `pull` create those.
38
+ - **The CLI carries this entire skill offline** — installing the devcli puts a copy of the pack on disk, pinned to that version. `vidfarm skill ls` lists it, `vidfarm skill show <path>` prints one file, and **`vidfarm skill search "<term>"` greps all of it at once**, which is the cheapest way to find the paragraph you need without loading a 650-line reference. No account, no network. It is documentation, not entitlement: the free-local half (clips, hyperframes, `serve` render, `qa`, harnesses, `dedupe`, local TTS/STT) runs offline; AI generation, hosted render, `recycle`, `download-video` and marketplace still need `vidfarm login` and a cloud call.
38
39
 
39
40
  ### Entity ID formats
40
41
 
@@ -129,7 +130,7 @@ If the user hasn't picked yet and you're about to spend, name the cheaper path a
129
130
 
130
131
  Cost mode answers *how much money may I spend*. It does not answer *how much of the user's own hands may I use* — and that second axis moves quality more than the first. **Ask both.** They are independent: every cost mode (`minimize`, `hybrid`, `rich-ai`, `pure-videogen`) runs in either interaction mode.
131
132
 
132
- - **interactive** — the user is willing to do a little manual work at fixed checkpoints, and the video gets better for it. Two checkpoints cover nearly everything: **(1) images** — you write a prompt, they run it in a *free* frontier web generator (meta.ai / ChatGPT / Gemini / a Hugging Face Space) and hand the file back; **(2) raw clips** — you hand over search keywords, they search TikTok/YouTube, download a few with a free online downloader, and point you at the folder.
133
+ - **interactive** — the user is willing to do a little manual work at fixed checkpoints, and the video gets better for it. Three checkpoints cover nearly everything: **(1) images** — you write a prompt, they run it in a *free* frontier web generator (meta.ai / ChatGPT / Gemini / a Hugging Face Space) and hand the file back; **(2) raw clips** — you hand over search keywords, they search TikTok/YouTube, download a few with a free online downloader, and point you at the folder; **(3) the voice** — you sample a few narrators and they pick the one the video sounds like (costs them 30 seconds and $0, see below).
133
134
  - **autonomous** — you finish end-to-end with zero steps from them: source clips yourself (browser control → `raws scan` → public raws), generate within the budget, or do without.
134
135
 
135
136
  **Why interactive usually wins on quality:** the free tiers of the frontier web image models are typically *better* than what an API-key budget buys per image, and a human eye picks better footage than any keyword scan. In `minimize` the gap is not incremental — it's the difference between **no custom art at all** and **a full sticker pack for $0**.
@@ -147,6 +148,10 @@ Cost mode answers *how much money may I spend*. It does not answer *how much of
147
148
 
148
149
  **In interactive mode, MANUAL IMAGE WORK DEFAULTS TO STICKER PACKS.** Never ask for one graphic per round trip — each hand-off costs the user a context switch and costs you tokens re-reading a file. Ask for **one sheet holding every graphic**, then split it locally for $0. `vidfarm handoff image --theme "<what>" --items "a,b,c"` mints the whole brief (prompt + steps + the free tools + the follow-up command); `--single` when you really do want one subject. When the file comes back: `vidfarm sticker-pack <sheet> --items "a,b,c"`.
149
150
 
151
+ **In interactive mode, OFFER THE VOICE CHECKPOINT — in every cost mode.** Who the video sounds like is a taste decision, and the default voice is the one choice agents make silently that a director almost always wants a say in. Before narrating, ask *"want to hear a few voices and pick one?"* and sample: `vidfarm voices --sample` (premium) or `vidfarm voices --free --sample` ($0 local). **Sampling costs nothing on either tier** — premium samples are ElevenLabs' own preview clips (a CDN download, not a synthesis call) and free samples render locally — so this checkpoint is just as available in `minimize` as in `hybrid`. Play the files, take their pick, narrate with `--voice <id>`. `vidfarm tts` prints the same nudge on stderr whenever narration would run with no voice named and the mode is interactive (or was never set).
152
+
153
+ **And say where the premium voices come from, because users assume wrong.** The full ElevenLabs catalog is reachable **through vidfarm's own ElevenLabs connection** — no ElevenLabs account, API key, or subscription on the user's side; narration just spends **vidfarm wallet credits** (pennies each). In `hybrid` that is a real option to put on the table next to the free voices, not a locked door. `--own-key` is only for users who already have an ElevenLabs key and would rather bill their own account.
154
+
150
155
  **Raw-clip sourcing has a ladder — hand it to the human only at the bottom rung.** In order: (1) **your own browser control**, if you have it — drive the search and download yourself; (2) **Vidfarm cloud** — `vidfarm clipper <url>` / `vidfarm raws scan <url> --cloud` resolves and mines the video for you; (3) the **free public raws catalog** — `vidfarm public-raws --category <shelf>`; (4) **the user**, when you're fully local/keyless or when human taste matters. That last rung is `vidfarm handoff raws --keywords "villa construction,pouring concrete" --platforms tiktok,youtube --purpose "<what the clips are for>"` — it prints the keywords, tells them to google a downloader (a *search*, not a link that rots), and names the import command for when the folder is ready (`vidfarm clipper ./downloads/<file>.mp4`, or `vidfarm raws scan` to mine a long one).
151
156
 
152
157
  ## Default stance
@@ -204,16 +209,20 @@ Present both harnesses to the director, recommend (A) unless they've asked for p
204
209
 
205
210
  **Explainer/cutout videos — the transparent-sticker workflow.** For explainers (a subject "on stage" while labels, arrows, and props pop in around it), the cheap workhorse is a **transparent cutout sticker**: `vidfarm cutout --generate "<subject>"` AI-generates the graphic on a chroma plate, keys it out, **and trims the canvas down to the subject's true min width/height** — one free local ffmpeg step, no mostly-empty PNG to fight with — then `vidfarm place` + `vidfarm keyframes` scale/position and animate it (zoom, grow, shake, drift). It's the same "generate a reusable element once, then reuse it" thrift as the cheap harness, tuned for stickers. Full guided harness: recipe `recipes/cutout-graphics-for-explainers.md`; placement + zoom/grow/shake/move motion recipes: `references/editor-workflows.md` (“Cutout graphics for explainers”).
206
211
 
207
- **"Make me a sticker pack" = ONE greenscreen sheet of many items, then masked apart — and `vidfarm sticker-pack` is that whole loop.** A sticker pack is never one graphic; it's a *set* (props, icons, reactions, characters, backdrops) that must share one art style. Generating them one at a time is both expensive (N image jobs) and inconsistent (N independent styles), so the move is the opposite: **generate a single image holding every item, laid out on a flat greenscreen plate, then cut each item out locally for $0.** `vidfarm sticker-pack --generate "<theme>" --items "a,b,c"` does all of it — one billed image job for the whole set, then a free local key, an **automatic** alpha-segmentation that finds each item (no hand-measured `--crop` rects), a per-item trim to its true bounding box, and a `stickers.json` manifest. Already have a greenscreen sheet? `vidfarm sticker-pack ./sheet.png` cuts it up for **$0**. Use `--dry-run` to eyeball the detected boxes first; `--gap` merges/splits items that came out joined or broken; `vidfarm mask <sheet> --crop …` is the manual fallback for one stubborn item.
212
+ **"Make me a sticker pack" = ONE greenscreen sheet of many items, then masked apart — and `vidfarm sticker-pack` is that whole loop.** A sticker pack is never one graphic; it's a *set* (props, icons, reactions, characters, backdrops) that must share one art style. Generating them one at a time is both expensive (N image jobs) and inconsistent (N independent styles), so the move is the opposite: **generate a single image holding every item, laid out on a flat greenscreen plate, then cut each item out locally for $0.** `vidfarm sticker-pack --generate "<theme>" --items "a,b,c"` does all of it — one billed image job for the whole set, then a free local key, an **automatic** alpha-segmentation that finds each item (no hand-measured `--crop` rects), a per-item trim to its true bounding box, and a `stickers.json` manifest. The sheet comes in two shapes: **zoned** (a grid of color panels, one plate color per item — the default for 2+ named items, and what frees the art from a single banned hue) and **flat** (the classic one-color plate, for a model that can't follow a color-block grid). Already have a greenscreen sheet? `vidfarm sticker-pack ./sheet.png` cuts it up for **$0**. Use `--dry-run` to eyeball the detected boxes first; `--gap` merges/splits items that came out joined or broken; `vidfarm mask <sheet> --crop …` is the manual fallback for one stubborn item.
208
213
 
209
214
  - **Stickers are not necessarily small.** A sticker is *any* transparent element you place and animate — an icon, a mascot, a prop, a character, and equally **a full-width landscape, skyline, or backdrop** that fills the frame. `sticker-pack` filters speckle only; it has no maximum item size. Ask for the big pieces in the same sheet as the small ones.
210
215
  - **Stickers are usually animated, not pasted.** Once placed, animate each one with `vidfarm keyframes` presets (`pop-in`, `float`, `shake`, `grow`, `slide-in-left`, `drift`) — that's HTML/CSS canvas motion, deterministic, free, and identical in preview and render. Layer moves up (pop-in, then idle float) for real life. See `references/editor-workflows.md` → "Cutout graphics for explainers".
211
216
  - **A sticker can carry its OWN motion too.** A *moving* subject has no single bounding box, so it isn't a PNG: key the clip with `vidfarm remove-greenscreen <video>` → transparent WebM (browser/editor-playable, the right choice on a composition).
212
- - **The plate must be a color the ART DOESN'T USE green is a default, not a law.** A chroma key removes exactly ONE color, so a green plate under green artwork (leaves, frogs, plants, money, an "eco" icon set) keys holes straight through the stickers. `sticker-pack`/`cutout` handle this for you in two ways, and you should still say it out loud when it matters: **(a)** when generating, they read the subject and move the plate off any hue it mentions green magenta (`#FF00FF`) blue (`#0047BB`) black white and print which plate they picked and why; **(b)** when splitting a sheet you already have, they **detect the plate from the sheet's four corners**, so a red/purple/blue sheet handed back from a web generator just works. Pin it yourself with `--key-color "#FF00FF"` / `--preset magenta` (that disables the auto-pick), or `--no-auto-key` to force plain green. If a pack's palette collides with *every* standard plate, split it into two sheets on two different plates rather than fighting the key.
213
- - **The ART must be CLOSED and SOLIDLY FILLED — this is the other half of surviving the key, and the #1 way stickers come back broken.** Ask an image model for "icons on a green plate" and it will happily draw **outline art**: a colored stroke with the shape's interior left as bare plate. It looks perfect on the sheet, and after the key each sticker is a **rim floating around a see-through hole** (an apple-shaped outline with nothing inside it). Same outcome from a *near-plate* fill (the keyer works on tolerance, not exact match), a translucent/glassy/glowing material, or a soft glow fading into the plate. **You cannot key those pixels back — it has to be in the prompt:** *"every object is a closed, solidly filled shape; outlines must enclose an opaque fill of a different color; no outline-only or hollow art; nothing on the art in the plate color or any near-shade of it; fully opaque, no translucency, glow or drop shadow."* `cutout --generate`, `sticker-pack --generate`, `handoff image` and the `create-overlay` primitive **append that clause for you** with the chosen plate hex — write it yourself only when you prompt a generator directly. After the key, both commands report per-item `hole_pct`/`hollow` (console `⚠ N% hollow`, `--json`, `stickers.json`) — a ring or picture frame reads the same way, so it **warns, never blocks**. Flagged and it shouldn't be? Re-generate with the fill clause; a *near*-plate fill can sometimes be rescued with a lower `--tolerance`; one stubborn item can be lifted with `vidfarm mask --crop …` (ONNX matting ignores fill color).
217
+ - **The key is CONNECTIVITY-based, so "the art can't use the plate color" is no longer true only its OUTER EDGE can't.** `sticker-pack`/`cutout` (and `remove-greenscreen <image> --smart`) don't delete every pixel that looks like the plate. They flood-fill the plate **inward from the edge of the sheet** and delete only background that **reaches** that edge. A green leaf inside a mascot, a plate-colored eye, an outline shape whose interior was left as bare plate none of it is reachable, so none of it is deleted. Edges are feathered and the plate is **un-mixed out of each edge pixel individually** (real alpha math, not a global `despill`), which is what kills the green fringe a flat key leaves. What still matters: the item's **silhouette** must be a different color from its own plate, and nothing may **fade** into the plate (no soft glow, blur or drop shadow on the background). Say the win out loud when it matters the console prints *"kept N plate-colored pixels INSIDE the art that a flat key would have punched out."* `--key-mode flat` restores the old plain-chromakey behaviour (the cloud path's exact filter chain use it to reproduce a cloud render, or as a simple fallback).
218
+ - **Plate color is still chosen for you, and it still matters for the silhouette.** When generating, `sticker-pack`/`cutout` read the subject and move the plate off any hue it mentions green magenta (`#FF00FF`) blue (`#0047BB`) black white printing which plate they picked and why. When splitting a sheet you already have, they **read the plate off the sheet itself**, so a red/purple/blue sheet from a web generator just works. Pin it with `--key-color "#FF00FF"` / `--preset magenta`, or `--no-auto-key` for plain green.
219
+ - **ONE PLATE COLOR PER STICKER — `--sheet-mode zoned`.** The real fix for "our art has to be simple because of the greenscreen" is to stop giving a whole sheet one background. A **zoned** sheet is a grid of solid color **panels**, one item per panel, each panel's plate chosen against **that item**: a green frog on magenta beside a pink flower on green, in one image job. Each panel is keyed independently with its own color (read back off that panel's own corners, because models drift the hue they were asked for), and item art may then use **any palette at all — including the color of a different panel**. Bonus: names stop being guessed from reading order — panel N holds the item you asked for in panel N, so `--items` maps exactly, and `stickers.json` records each sticker's `panel` and `plate`. `--sheet-mode auto` (the default) zones a generation of 2+ named items and stays flat otherwise. Reading a zoned sheet you already have: `--zones auto` (default — recovers the grid from the sheet's own edges) or `--zones 3x2`.
220
+ - **The FLAT single-color sheet is still first-class — use it for a weaker image model.** Not every model can hold a color-block grid; a cheap or small one will paint one background regardless of the prompt. That path is fully supported: `--sheet-mode flat` asks for the classic one-plate sheet, and even if you asked for zones, keying **detects a model that ignored the grid** (a panel with no plate to remove, or one whose item filled it corner to corner) and **automatically re-keys the sheet as one plate**, telling you it did. So zoning can't strand you — worst case you're back on the simple method, still for $0.
221
+ - **Painterly, soft, furry or glassy art → `--refine`.** Chroma keying of any kind needs a crisp silhouette. When the art has genuinely soft edges (watercolor, fur, glow, glass, a cast shadow), add `--refine`: after the keyer locates each item, that item is re-cut from the **un-keyed** sheet with the local ONNX matting model (free, ~1–2s each), which is color-blind and handles soft mattes. It sanity-checks each matte and falls back to the keyed cut per item if the model didn't find a subject — flat vector art is exactly the case where matting fails and the chroma cut is better, so don't reach for `--refine` by default.
222
+ - **What the ART still has to honor: a crisp silhouette, sealed shapes, and gaps between items.** The connectivity key removes the old bans on hollow art and plate-colored fills, but three prompt rules are still load-bearing: **(1)** every object's **outer edge** is a clearly different color from its own plate and is **crisp** — no glow, blur, mist or drop shadow fading into the background; **(2)** enclosed areas are **sealed by the artwork**, because a gap in an outline lets the background flow in and the fill really does get keyed; **(3)** items are separated by a clear margin of plate — two touching items segment as ONE sticker. `cutout --generate`, `sticker-pack --generate`, `handoff image` and the `create-overlay` primitive **append the right clause for you** (a relaxed one for the smart keyer, the strict "closed, solidly filled, nothing in a near-plate shade" one when `--key-mode flat` is in play) — write it yourself only when prompting a generator directly. Both commands still report per-item `hole_pct`/`hollow` (console `⚠ N% hollow`, `--json`, `stickers.json`); under the smart keyer a flagged item is *usually real* (a ring, frame, donut, or pieces with background between them), so it **warns, never blocks**. One stubborn item can always be lifted with `vidfarm mask --crop …` (ONNX matting ignores color entirely).
214
223
  - **Transparent GIF is supported, for GIF-only surfaces.** `vidfarm sticker-pack … --output-format gif` (stills) and `vidfarm remove-greenscreen <video> --gif` (animated) emit transparent GIFs. GIF alpha is **1-bit**, so edges go hard — fine for chat/forum/Notion sticker surfaces, worse than PNG/WebP/WebM for compositing on a timeline. Prefer PNG/WebP/WebM unless the destination only eats GIF.
215
224
 
216
- **Explainer house style — the defaults to build with unless told otherwise.** **White background / light mode** (plain white stage, no gradients, no dark mode, no photo backdrop), **kinetic word-by-word captions** in dark ink on the light stage (`vidfarm captions generate --style word-pop --color "#111111" --active-color "#7C3AED" --background-style plain` — skip outlines/shadows, they're only needed over busy footage), and **female TTS narration** (`vidfarm tts --voice coral` on OpenAI — `nova` for energy, `sage` for calm; `Kore`/`Leda` on Gemini; any ElevenLabs voice via `vidfarm voices`). **Keep it clean and simple** — one idea on screen at a time, two or three cutouts per beat, one accent color, one font, lots of white space; remove before you add. **Illustrations default to simplicity**: flat vector, simple shapes, minimal detail, 2–3 flat colors, no baked-in text — simple art keys cleanly, trims tight, and stays on-style across the whole cast. State the defaults once so the director can override any of them. Full detail: recipe `recipes/cutout-graphics-for-explainers.md` (“House style — the explainer defaults”). **If the director takes the stage off white**, two things stop being optional: every sticker's **white die-cut rim** has to be stripped (on a dark stage it's a glaring halo and the most obvious bot-made artefact in the frame — recipe → “Stickers on a DARK or photographic stage”), and the caption hexes above stop applying — **caption colour, active-word colour and plate are chosen by measuring the composited background behind the caption band**, one treatment per video (`regimes/short-form.QA_REGIME.md` → “Caption styling is MEASURED off the background”). Related: **on-screen text and captions must not say the same thing at once** — display text carries the argument, captions carry only what the screen doesn't show.
225
+ **Explainer house style — the defaults to build with unless told otherwise.** **White background / light mode** (plain white stage, no gradients, no dark mode, no photo backdrop), **kinetic word-by-word captions** in dark ink on the light stage (`vidfarm captions generate --style word-pop --color "#111111" --active-color "#7C3AED" --background-style plain` — skip outlines/shadows, they're only needed over busy footage), and **female TTS narration** (`vidfarm tts --voice coral` on OpenAI — `nova` for energy, `sage` for calm; `Kore`/`Leda` on Gemini; any ElevenLabs voice via `vidfarm voices`). **Keep it clean and simple** — one idea on screen at a time, two or three cutouts per beat, one accent color, one font, lots of white space; remove before you add. **Illustrations default to simplicity**: flat vector, simple shapes, minimal detail, 2–3 flat colors, no baked-in text — simple art keys cleanly, trims tight, and stays on-style across the whole cast. State the defaults once so the director can override any of them. Full detail: recipe `recipes/cutout-graphics-for-explainers.md` (“House style — the explainer defaults”). **If the director takes the stage off white**, two things stop being optional: every sticker's **white die-cut rim** has to be stripped (on a dark stage it's a glaring halo and the most obvious bot-made artefact in the frame — recipe → “Stickers on a DARK or photographic stage”), and the caption hexes above stop applying — **caption colour, active-word colour and plate are chosen by measuring the composited background behind the caption band**, one treatment per video (`harnesses/short-form.HARNESS.md` → “Caption styling is MEASURED off the background”). Related: **on-screen text and captions must not say the same thing at once** — display text carries the argument, captions carry only what the screen doesn't show.
217
226
 
218
227
  **Landscape footage in a fullscreen vertical explainer — use the blurred plate, never bars.** When an explainer is built on **real filmed footage** and the source is 16:9 (or 4:3) on a 9:16 canvas, do not `contain` it (hard black letterbox bars read as an unfinished export) and do not blindly `cover` it (a wide shot loses its left and right thirds). Duplicate the clip: a full-canvas `cover` copy behind, heavily **gaussian-blurred and faded dark**, plus the sharp copy centered as a hero band — optionally zoomed ~1.3× — with its **top and bottom edges feathered** into the blur. Same clip, same timecode, so it reads as one continuous image with a shallow-depth-of-field plane, fullscreen edge to edge, nothing cropped, and clean dark space for the header and captions. Bake it once with ffmpeg into a single 1080×1920 file (free, local) and place it as one ordinary full-canvas layer — layer blur is not an editor property, so the pre-bake is the path that works in the editor, `serve`, and cloud render alike. Copy-paste ffmpeg + HTML recipes, tuning table, and the failure modes: `references/editor-workflows.md` (“The blurred plate — landscape footage, fullscreen, on a vertical canvas”).
219
228
 
@@ -282,32 +291,63 @@ You may be running as the **in-web AI chat** (the /editor copilot, the chat dock
282
291
  - **Determine the surface before claiming capabilities.** The web chat has only its declared tools and REST routes. It cannot execute arbitrary JavaScript/Python, open a shell, create a local repository script, or use the user's filesystem. Never tell a web-chat user that you ran code or wrote a script unless a dedicated declared tool actually did so. A desktop coding agent has a real shell and filesystem and MAY write/run scripts, perform arbitrary local computations over paginated API results, create reports/CSVs/JSON, edit composition files, and orchestrate long devcli workflows within the user's authorization.
283
292
  - **The web AI chat can do all three paintbrushes** — clip raws, author HTML/hyperframe motion, and generate AI media — and it drives edits directly on the live timeline. Keep small-to-medium jobs here: text/caption swaps, a scene or two replaced, single generations, captions, approve/schedule. Just do them.
284
293
  - **No HTML slop — a video is not a web page.** Compositions are authored in HTML, so the #1 tell of an AI-edited video is landing-page furniture: gradient CTA "buttons" ("Sign Up for a Free Trial →"), rows of benefit chips/badges ("✓ No Credit Card Needed"), frosted/bordered cards holding a gradient headline + a URL, feature grids, bullet lists, web-default type (Inter/Roboto/Arial at weight 400-600). None of that exists in a real TikTok/Reel — **nothing in a video is clickable**. **The test is the native-editor test: could you have made this element with the tools inside TikTok's own editor?** That toolset is font / color / stroke / shadow / tight text box / alignment / rotation / animation presets, plus stickers, emoji, drawn marks and clips — it has no padded capsule, no border, no gradient fill, no blur panel, no card. If you reached past it, cut it. That includes **a single lonely pill around a stat or label** — `( 10 hrs / week )`, `( STEP 2 )`, `( EP.01 )`: being alone doesn't make a badge native, and the only legitimate capsule in a video is the active-word `spotlight`/`karaoke` highlight, which moves with the spoken word. Emphasize a stat the way the editor would instead: bigger, heavier, ALL-CAPS, an accent color, a hand-drawn circle, or its own beat. If you are typing `btn` / `badge` / `chip` / `card` / `rounded-full` / `shadow-lg` / `backdrop-blur` / `bg-gradient-to-r` — or a `border-radius` over ~8px on anything filled that holds words — stop and rewrite it as timed text on the footage. Arrows, scribble/underline marks, italics, ALL-CAPS, single-word color pops, emoji, transparent cut-out stickers, and mock social UI (iMessage bubbles, comment cards) are all *fine* — they're native to the platform.
285
- - **Every video gets the four charges — hook, loop, payoff, bait — and you write them BEFORE you touch the timeline.** This is the largest quality delta in the product and it costs nothing: most agent-made videos fail on structure, not polish, because the timeline is the fun part so it gets built first and the words get retrofitted. Invert it: (1) write the **hook line as text** — a complete clause, subject + verb, no jargon, naming a **situation** ("I've quit six businesses"), never a label ("anonymity") — and put it on screen at `start:0`; (2) name the **curiosity loop** and the timestamp it closes at, *inside this video* (if you can't state the timestamp, there is no loop, and the withheld answer must be one the viewer **can't guess**); (3) name the **payoff** — shown, not summarized, landing before the final beat; (4) write the **bait** — one ask in the final beat and in the post caption. Then build. Chunk 1 is read before any audio (muted autoplay is the default), so the text hook does more work than the spoken one. Full harness — the three gates, banned openers, loop mechanics, compliance, and diagnosis-by-charge — in `references/hooks-and-virality.md`; the checkable form is `vidfarm regime show hooks`.
294
+ - **Every video gets the four charges — hook, loop, payoff, bait — and you write them BEFORE you touch the timeline.** This is the largest quality delta in the product and it costs nothing: most agent-made videos fail on structure, not polish, because the timeline is the fun part so it gets built first and the words get retrofitted. Invert it: (1) write the **hook line as text** — a complete clause, subject + verb, no jargon, naming a **situation** ("I've quit six businesses"), never a label ("anonymity") — and put it on screen at `start:0`; (2) name the **curiosity loop** and the timestamp it closes at, *inside this video* (if you can't state the timestamp, there is no loop, and the withheld answer must be one the viewer **can't guess**); (3) name the **payoff** — shown, not summarized, landing before the final beat; (4) write the **bait** — one ask in the final beat and in the post caption. Then build. Chunk 1 is read before any audio (muted autoplay is the default), so the text hook does more work than the spoken one. Full harness — the three gates, banned openers, loop mechanics, compliance, and diagnosis-by-charge — in `references/hooks-and-virality.md`; the checkable form is `vidfarm harness show hooks`.
295
+ - **Then CUT it — every second must earn its place, and most don't.** Assume your first assembly is **30–50% too long**. Run the **deletion test** on every beat: delete it; if the video still makes sense and the payoff still lands, it stays deleted. Whatever survives must serve one of the four charges — "it gives context" is not a charge. Cut on sight: intros/logo stings, the wind-up sentence before the claim ("so I wanted to talk about…"), restatement, inter-sentence silence over ~0.35s, real-time process, establishing shots, reading what's already on screen, and any tail after the last word. **Always ripple the hole closed** (`vidfarm ripple <dir> --at <sec> --delta -<sec>`) — a cut that leaves a gap turns fluff into dead air, which is worse. Density is **not** speed: the held comedic beat, the payoff playing out, and a cue's readability keep their seconds (cut *words*, not the time text is on screen). Length is an **output**, not a plan — a brief that dictates a duration ordered fluff. `vidfarm qa` flags the mechanical half (`dead-air`, `dead-tail`, `slow-scene`); the craft is `references/hooks-and-virality.md` → "Density".
286
296
  - **The first frame IS the thumbnail — compose it on purpose.** Frame 0 is a single frame of ~30 in the first second, but it's the poster every feed, share link, and paused player freezes on, so **more people see that one frame than watch the video**. It must never be black, empty, mid-fade, or caught mid-animation: put a real visual at `start:0`, have the hook words already on screen at t=0, and never hang a `fade-black`/`fade-white`/`flash` *entrance* on the **first** clip (junction transitions between later clips are fine — this rule is only about the opening). Verify it, don't assume: devcli `vidfarm stills ./work --at 0` renders that exact frame, and `vidfarm qa` flags a blank or fading open.
287
- - **Ask early: one-time video, or bulk?** "Make me a video about X" and "I need to post daily / give me 20 hook variants" are different jobs, and directors often don't know the second one has a name. Ask once, up front: *"One video, or should we set this up as a repeatable batch?"* Bulk = **scripting mode** (a pinned base fork + a loop that varies ONE thing per variant; a public-raws shelf is the cheapest source of the N), and every batch gets a **`QA_REGIME.md`** — because a loop of fifty videos has no human looking at every frame, and the regime is what replaces those eyes. Don't silently ship a one-off when they asked for volume, or drag someone into a harness when they wanted one clip.
288
- - **`QA_REGIME.md` is the director's own quality contract, and it's a first-class artifact.** `vidfarm qa`'s built-ins are universal (slop, fonts, the thumbnail frame); a regime is what makes *this* format good audience, hook shape, banned vocabulary, pacing, compliance line. It lives next to the work, they own it, it stacks: `vidfarm regime init short-form --out ./work/QA_REGIME.md` (bundled bases: `short-form`, `hooks`, `ugc-testimonial`, `explainer`, `product-demo` each a starting point to **edit**, never a house style), then `vidfarm qa ./work --regime hooks --regime ./brand/HOUSE.md`, and any user file anywhere is valid. Its `checks:` front matter is machine-settled; its `- [ ]` checklist comes back as **review items you answer honestly in your report** never claim a video passed the half the CLI can't judge. When a batch teaches you something, **write it back into the regime**: that's the artifact that compounds. Details in `references/automation-and-local-dev.md` ("Scripting mode"), format in `regimes/README.md`.
297
+ - **Ask early: one-time video, or bulk?** "Make me a video about X" and "I need to post daily / give me 20 hook variants" are different jobs, and directors often don't know the second one has a name. Ask once, up front: *"One video, or should we set this up as a repeatable batch?"* Bulk = **scripting mode** (a pinned base fork + a loop that varies ONE thing per variant; a public-raws shelf is the cheapest source of the N), and every batch gets a **`HARNESS.md`** — because a loop of fifty videos has no human looking at every frame, and the harness is what replaces those eyes. Don't silently ship a one-off when they asked for volume, or drag someone into a harness when they wanted one clip.
298
+ - **"Harness" is a known noun with a known process recognise it and follow it.** A **harness** is the reusable AI apparatus for ONE format or template: what makes it special, written down as `HARNESS.md` so an agent can reproduce it without the director in the room. It is a first-class artifact — the director owns it, edits it, versions it, and hands it to the next agent. Three phrasings, one artifact:
299
+ - **"create me a harness"** / "set up a harness for this format" → `vidfarm harness init <base> --out ./work/HARNESS.md` (bases: `short-form`, `hooks`, `ugc-testimonial`, `explainer`, `product-demo`), then **edit it with them**. The bundled file is a starting point, never a house style; the parts that matter are the ones they add — who the viewer is, the banned vocabulary, the compliance line, the pacing this account actually uses. A harness nobody edited isn't about their videos.
300
+ - **"update the harness for this format/template"** → open the existing `HARNESS.md` and write the new rule in, **with its reason on the same line** (a rule whose "why" is missing gets argued away by the next agent). This is what you do every time a batch teaches you something ("the label-framed hooks all died"): the compositions are disposable, the harness is the artifact that compounds.
301
+ - **"give me the harness for this template_id"** → they mean **the decomposition**: `vidfarm harness derive <templateId|forkId>`. It distils the decompose pass's DNA into an editable `HARNESS.md`. If the template hasn't been decomposed, run `vidfarm decompose` first.
302
+ **A harness mirrors the template JSON's own vocabulary** — `## Viral DNA` (hook / retention / payoff / emotion), `## Visual DNA` (cut rhythm, typography, b-roll, transitions), `## Structural DNA` (the beats, and which are load-bearing), `## Audio DNA` (voice, bed, comedic timing), `## Build DNA` (which paintbrush per beat) — the same strands the decompose pass writes as `viral_dna`, `visual_dna`, and friends. `vidfarm harness show <ref> --dna visual` prints one strand instead of the whole doc.
303
+ **Two halves, and only one is machine-checkable.** The `checks:` front matter is settled deterministically by `vidfarm qa` (duration, aspect, `hook_words_max`, `forbid_text`, …); every `- [ ]` line comes back as a **review item you answer honestly in your report** — never claim a video passed the half the CLI can't judge. Harnesses stack and auto-discover: `vidfarm qa ./work` picks up `./work/HARNESS.md`, `--harness hooks --harness ./brand/HOUSE.md` adds more, and any file of theirs anywhere is valid. Format and strand table: `harnesses/README.md`; scripting-mode detail: `references/automation-and-local-dev.md`. *(Formerly `QA_REGIME.md` — same file, and `vidfarm regime …` still works as an alias.)*
289
304
  - **A video is judged as a SEQUENCE, so review it as one.** Agents build scene by scene and each scene passes in isolation while the video drifts — inconsistent margins, three type sizes, an accent colour that wanders, beats that are all the same length, a jarring join. Tile a dozen stills into one contact sheet (`vidfarm stills ./work --sheet`) and read it as an image before you call anything done, fix drift by defining the system rather than patching the odd scene out, and remember that **your own confident "verified, looks good" is the single least reliable signal in this workflow** — it was wrong on every video of a 32-video batch. Method: `references/reviewing-renders.md`.
290
305
  - **On devcli, QA every video you produce: `vidfarm qa ./work`.** Free, instant, local-only — it blocklists exactly the slop above plus first-frame/thumbnail and font-regime/safe-zone drift, and prints a concrete fix per finding. **Feedback, not a gate**: it exits 0 even on findings, never runs automatically, and is a blocklist (unusual/stylized compositions pass untouched), so there's no reason not to run it before every publish. `--json` for scripted batches, `--strict` only if you want a CI failure. **Web-chat copilot: this command does not exist for you** (devcli-only, no REST twin) — apply the standard by hand, and when handing a heavy job to a local coding agent, tell them to run `vidfarm qa`.
291
- - **Every production adheres to the TikTok-native caption standard.** On-screen text lives inside the readable safe zone (**~8%–85%** of a 9:16 frame — never pinned to the top/bottom edges the phone UI clips), uses the composition's bold font regime (**Montserrat** default / TikTok Sans, weight **700–900**, ~36–64px on a 1080-wide frame), and uses **exactly one of four valid backgrounds**: outline/stroke (`background_style:"outline"`, the default), plain + shadow (`"plain"`), an active-word highlight pill (`set_captions` `spotlight`/`karaoke` — the only legitimate pill *in the whole frame*, static labels included), or a tight-hugging solid band (`"highlight-solid"`, radius ≤8px, no border/shadow/gradient/blur). Decomposed forks often inherit the source's edge-pinned caption in an off-regime font — fix it, don't inherit it. Local devcli renders auto-normalize position + font family only (never the slop), so author it correctly. Full rules in `references/editor-workflows.md` ("Social-native visual standard" + "TikTok-native caption standard").
306
+ - **Every production adheres to the TikTok-native caption standard.** On-screen text lives inside the readable safe zone (**~8%–85%** of a 9:16 frame — never pinned to the top/bottom edges the phone UI clips) and, inside that band, is **placed in the emptiest part of the frame** rather than dumped on the default lower third (read a still first — `vidfarm stills ./work --at <t>`; words over open sky or a blank wall beat words over the subject, and usually need no plate at all). Long narration is **paged into 3–5-word kinetic cues** (`vidfarm captions generate --style word-pop`), never one static wall of text. It uses the composition's bold font regime (**Montserrat** default / TikTok Sans, weight **700–900**, ~36–64px on a 1080-wide frame), and uses **exactly one of four valid backgrounds**: outline/stroke (`background_style:"outline"`, the default), plain + shadow (`"plain"`), an active-word highlight pill (`set_captions` `spotlight`/`karaoke` — the only legitimate pill *in the whole frame*, static labels included), or a tight-hugging solid band (`"highlight-solid"`, radius ≤8px, no border/shadow/gradient/blur). Decomposed forks often inherit the source's edge-pinned caption in an off-regime font — fix it, don't inherit it. Local devcli renders auto-normalize position + font family only (never the slop), so author it correctly. Full rules in `references/editor-workflows.md` ("Social-native visual standard" + "TikTok-native caption standard").
292
307
  - **Where the web chat struggles: complex, long, multi-step transformations.** A full multi-scene re-theme, an iterative render-critique-iterate loop, heavy scripted or batch work, or anything needing a real filesystem and many sequential tool calls will hit context limits, turn/timeout ceilings, and the web editor's constraints (CSS/declarative motion only — JS animation adapters are stripped on save). Don't grind a big transformation one layer at a time in a chat turn and stall.
293
308
  - **Practical workaround — hand the heavy job to local devcli.** When a task is genuinely large or long-running, **proactively recommend the director run it locally with an AI coding agent** (Claude Code / OpenAI Codex / any capable agent): `vidfarm pull <forkId>` writes the composition + the `.harness/` grounding bundle to disk, the agent edits with the full devcli verb set and JS animation adapters, renders free with `vidfarm serve`, and `vidfarm publish` pushes it back. This is the **best-quality (B) harness's** natural home (adversarial grading with a coding agent). Frame it as "this is a big rebuild — you'll get a better, faster result running it locally with a coding agent; here's how," not as a dead end.
294
309
  - **Offer a handoff, do not impersonate the desktop agent.** When web chat reaches that boundary, offer to save a Markdown handoff in My Files containing the objective, selected template/fork IDs, asset paths, grounding, constraints, completed work, and suggested devcli commands. Create it only after the user agrees. The desktop agent should read that document, pull the referenced fork, and then use its actual code/shell capabilities.
295
310
  - **Never send the user away just to read knowledge.** Deeper skill knowledge is always a **tool call** away in-place: call `load_skill` (e.g. `load_skill('vidfarm', file='references/editor-workflows.md')`, or a craft pack like `editor-capabilities` / `hyperframes-animation`) to pull the exact reference you need mid-conversation. Only recommend switching surfaces for the WORK (a heavy transformation), never for the information.
296
311
 
297
- ## Read Only What You Need
312
+ ## File Index everything in this pack, and when to read it
313
+
314
+ **This is the complete inventory. Nothing else exists in the pack, and every file here is reachable by name.** Read the narrowest file that answers the question; never preload several. `size` is a context-cost estimate — the four big references are real reads, so pick one deliberately rather than sweeping them.
298
315
 
299
- Read only the relevant reference file for the current task.
316
+ **References the broad knowledge domains**
300
317
 
301
- - Template discovery, auth, fork/publish/share/cost flow: `references/core-workflows.md`
302
- - Timeline editing, decompose, captions, motion, AI placement: `references/editor-workflows.md`
303
- - Raws hunts, My Files, recurring characters, asset retrieval: `references/assets-and-sourcing.md`
304
- - REST automation, `vidfarm` command surface, local serve loop, skill packs: `references/automation-and-local-dev.md`
305
- - Getting-started interviews, **consultations**, strategy docs, onboarding flow: `references/onboarding.md`
306
- - Primitive routes such as TTS, STT, music, overlays, background removal, product placement: `references/primitives.md`
307
- - **Hooks, retention, and why a video travels — the four charges: `references/hooks-and-virality.md`.** Read it before writing ANY hook, caption script, or re-theme, and before a hook-variant batch. It is the craft harness; the rest of the skill is mechanics.
308
- - **Reviewing a finished render the holistic pass, the six common defects, frozen-render and audio verification: `references/reviewing-renders.md`.** Read it before you report a video as done, and before you grade someone else's.
309
- - Bulk / N-variant production: recipe `recipes/bulk-scripting-with-a-regime.md`; the `QA_REGIME.md` format and the bundled bases: `regimes/README.md` (+ the regime files beside it, readable as-is)
310
- - Complete REST API map and raw-HTTP conventions: `references/rest-api.md`. Load it only when the user asks for REST, an endpoint/schema, direct HTTP integration, or exhaustive API coverage. For the entire specification, follow its domain links and load every listed reference; do not preload them into ordinary director conversations.
318
+ | File | Size | Read it when |
319
+ |---|---|---|
320
+ | `references/core-workflows.md` | ~360 ln | Template discovery, auth, fork → render → approve → share, versioning, cost/wallet, marketplace orders, dedupe-before-publish |
321
+ | `references/editor-workflows.md` | ~650 ln | **The biggest read.** Timeline editing, decompose, captions, transitions, motion, AI placement, the caption standard, the editor action verbs |
322
+ | `references/assets-and-sourcing.md` | ~185 ln | Raws hunts, clip scanning, My Files, recurring characters, downloading media off a URL, social recycle |
323
+ | `references/automation-and-local-dev.md` | ~520 ln | **Big.** The whole `vidfarm` command table, REST automation, scripting/bulk mode, `HARNESS.md`, local serve loop, skill packs |
324
+ | `references/primitives.md` | ~475 ln | **Big.** One-shot primitive routes: TTS, STT, music, avatars, overlays, greenscreen, inpaint, background removal, product placement |
325
+ | `references/hooks-and-virality.md` | ~295 ln | **Before writing ANY hook, caption script, or re-theme**, and before a hook-variant batch. The four charges, three gates, banned openers, loop mechanics. This is the craft; the rest of the pack is mechanics |
326
+ | `references/reviewing-renders.md` | ~140 ln | **Before you report a video as done**, or grade someone else's. The holistic pass, the common defects, frozen-render and audio verification |
327
+ | `references/onboarding.md` | ~30 ln | Cold-start interviews, **consultations** (the `brainstorm/*` chain), strategy docs, durable director context |
328
+ | `references/rest-api.md` | ~85 ln | Only when the user asks for REST, an endpoint/schema, or direct HTTP integration. It is an index — follow its domain links; do not preload it into ordinary director conversations |
329
+
330
+ **Recipes — step-by-step procedures. When a recipe matches the task, prefer it over the broad reference.**
331
+
332
+ | File | Size | Read it when |
333
+ |---|---|---|
334
+ | `recipes/find-and-fork-template.md` | ~15 ln | Template selection and the first fork |
335
+ | `recipes/retheme-template.md` | ~15 ln | Full re-theme that preserves the source format's feel |
336
+ | `recipes/local-edit-render-approve.md` | ~20 ln | The local pull → edit → render → approve loop |
337
+ | `recipes/onboard-a-new-director.md` | ~15 ln | New-director onboarding and durable context capture |
338
+ | `recipes/bulk-scripting-with-a-harness.md` | ~100 ln | **Volume**: daily posting, N variants, hook tests — scripting mode with a `HARNESS.md` |
339
+ | `recipes/cutout-graphics-for-explainers.md` | ~265 ln | Building an explainer from sticker/cutout art: the house style, sticker sheets, keying, dark-stage rules |
340
+
341
+ **Harnesses — the `HARNESS.md` format and its bundled bases.** All are readable as-is and copyable with `vidfarm harness init <name>`.
342
+
343
+ | File | Size | Read it when |
344
+ |---|---|---|
345
+ | `harnesses/README.md` | ~110 ln | **Start here for anything harness-shaped**: the three director phrasings, the format, the `checks:` key list, the DNA strand → decompose-JSON map |
346
+ | `harnesses/short-form.HARNESS.md` | ~225 ln | The default base. Also holds the **"Caption styling is MEASURED off the background"** procedure that other files point at |
347
+ | `harnesses/hooks.HARNESS.md` | ~120 ln | Hook-variant batches — chunk-1 legibility, the unguessable test, volume-only anti-patterns. The checkable form of `hooks-and-virality.md` |
348
+ | `harnesses/explainer.HARNESS.md` | ~100 ln | Faceless educational video: one claim, invented visuals |
349
+ | `harnesses/ugc-testimonial.HARNESS.md` | ~90 ln | A person vouching for a product — mostly rules about what NOT to add |
350
+ | `harnesses/product-demo.HARNESS.md` | ~110 ln | Real product doing a real thing; the highest slop-risk format in the catalog |
311
351
 
312
352
  ## HyperFrames Skills — Load on Demand
313
353
 
@@ -323,9 +363,9 @@ On the web copilot, call `load_skill('<name>')` and load referenced files only w
323
363
 
324
364
  HyperFrames authoring and rendering in this package are Vidfarm-native: local work uses the bundled composition toolchain and `vidfarm serve`; cloud work uses Vidfarm render routes. Do not require an external vendor account, repository, publish service, or telemetry endpoint. Keep `HYPERFRAMES_SKIP_SKILLS=1` and `HYPERFRAMES_NO_TELEMETRY=1` in Vidfarm-managed environments so the bundled skills stay pinned and local work does not phone home.
325
365
 
326
- ## Quick Router
366
+ ## Quick Router — from what the user said to what to open
327
367
 
328
- Choose the narrowest path that satisfies the request.
368
+ The File Index above says what each file *is*; this says which one a given ask means. Choose the narrowest path that satisfies the request.
329
369
 
330
370
  1. If the user needs help figuring out what to make, **or asks for a "consultation"** (the `brainstorm/*` chain: cold-start interview → awareness stages → angles → hooks), read `references/onboarding.md` first.
331
371
  2. If the user already knows the goal and needs a suitable template, read `references/core-workflows.md` and use the template discovery flow.
@@ -334,7 +374,9 @@ Choose the narrowest path that satisfies the request.
334
374
  4b. If the task is **“download this video/audio off a website”** (a pasted YouTube / TikTok / Instagram / X post URL the user wants the actual file from), Vidfarm does that for you on a **paid plan** — `POST /api/v1/primitives/videos/download` (or `/audio/download`), devcli `vidfarm download-video <url>` / `vidfarm download-audio <url>`. **Free plan → do not call it; walk the user through opening the URL in Chrome and downloading it from the page, then `vidfarm put-file` the local file in for $0.** Details in `references/assets-and-sourcing.md` → `references/primitives.md`.
335
375
  4c. If the task is **“turn this Reddit/X thread, subreddit, or account into a video”** — “tweet to TikTok”, “Reddit to TikTok”, “make a video from this thread”, “what are the top comments saying” — run `vidfarm recycle <source>` (or `POST /api/v1/primitives/social/recycle`) with the URL. It **decomposes** the source into raw JSON (text, comment tree, media URLs, author pics, stats) and hands it back unranked so YOU pick what to remix. **Paid plan; `max_records` is the spend ceiling.** Brokers the reddit-lead-gen / x-lead-gen OfficeX apps, so it waits out their async job for you. Details in `references/assets-and-sourcing.md` → `references/primitives.md`.
336
376
  4d. If the task is **“post this again / to several accounts / on another platform”**, or you are about to publish or bulk-produce at all — that is **deduplication**. Run `vidfarm dedupe <mp4> [--variants N]` on the **exported file** (free, local ffmpeg, no re-render), then approve/schedule each variant. **Ask the operator whether they want deduplicated copies, and how many, BEFORE the render/bulk run** — deciding after means paying for a second render. Details in `references/core-workflows.md` → *Deduplicate before you publish* and `references/primitives.md` → *Primitive: media_dedupe*.
377
+ 4e. If the ask contains the word **“harness”** — *“create me a harness”*, *“update the harness for this format”*, *“give me the harness for this template_id”* — that is a known, named process, not a vague request. Read `harnesses/README.md` (the three phrasings and the format), then `recipes/bulk-scripting-with-a-harness.md` if the job is a batch. The third phrasing means the **decomposition**: `vidfarm harness derive <forkId>`.
337
378
  5. If the task is scripted, local, CI-driven, or `vidfarm serve`-based, read `references/automation-and-local-dev.md`.
379
+ 5b. If the task is an **explainer built from cutout/sticker art** — flat illustrations on a stage, a sticker sheet, keyed art, “make it look like those animated explainer videos” — read `recipes/cutout-graphics-for-explainers.md`. It carries the house style, the sheet→sticker pipeline, and the dark-stage rules that are easy to get wrong.
338
380
  6. If the task explicitly asks for a primitive or needs specialized generation/transcription work, read `references/primitives.md`.
339
381
  7. If the task is the MARKETPLACE (ordering videos from specialist agents): browsing is web-only for paying customers — send the human to https://vidfarm.cc/marketplace, never render it locally. Placing/listing orders is the thin REST wrapper in `references/core-workflows.md` (§ Marketplace); anything deeper on a gig (inbox, proofs, payouts) needs the external Dollar Platoon skill — `npx skills add https://github.com/OfficeXApp/dollarplatoon-skill` — the same way FlockPoster work beyond scheduling needs `npx skills add https://github.com/OfficeXApp/flockposter-skill`.
340
382
 
@@ -350,21 +392,12 @@ Choose the narrowest path that satisfies the request.
350
392
  - **Never render or approve without judging frame 0 as a standalone still.** It is the thumbnail everywhere the post appears; an empty/black opening frame ships a dead post. See “The FIRST FRAME is the thumbnail”.
351
393
  - **Never judge the VIDEO by one frame, and never report a render as reviewed without the holistic pass.** Compare frames from at least two different scenes (a frozen render passes every other check), read a contact sheet for balance/spacing/style/pacing drift, and state separately what you measured vs. what you judged. See “Judge the WHOLE video”.
352
394
 
353
- ## Recommended Recipes
354
-
355
- Use these when the user’s task matches the pattern closely.
356
-
357
- - Template selection and first fork: `recipes/find-and-fork-template.md`
358
- - Full re-theme while preserving the format’s feel: `recipes/retheme-template.md`
359
- - Local pull/edit/render/approve loop: `recipes/local-edit-render-approve.md`
360
- - New-director onboarding and durable context capture: `recipes/onboard-a-new-director.md`
361
-
362
395
  ## Output Posture
363
396
 
364
397
  - Prefer concrete actions over abstract discussion.
365
398
  - Name the chosen path explicitly: template reuse, raws hunt, local serve, cloud render, etc.
366
399
  - Surface cost tradeoffs before expensive generation.
367
- - When in doubt between a broad reference and a recipe, start with the recipe.
400
+ - When in doubt between a broad reference and a recipe, start with the recipe — the File Index marks which is which.
368
401
 
369
402
  ## Mental model
370
403
 
@@ -1245,16 +1278,20 @@ Compositions are authored in HTML, so the single most common way an AI-edited vi
1245
1278
  - **Emoji inline in text** (sparingly), **sticker/cut-out overlays** on transparent PNG (`create-overlay`), mock social UI when the format calls for it (iMessage bubbles, a TikTok comment card, a fake DM, a countdown/progress bar) — these are native artifacts of the platform, not web furniture.
1246
1279
  - **Full-bleed footage** with text sitting directly on it.
1247
1280
 
1248
- **On devcli there's a checker: `vidfarm qa <dir|composition.html>`.** Free, instant, local-only — a blocklist pass for everything above plus the font regime and safe zone, with a concrete fix per finding. **Run it on every video you produce.** It is feedback, not a gate (exit 0 even on findings, never runs automatically, `--strict` only if you want a CI failure) and a blocklist, not an allowlist (stylized/hand-made compositions pass untouched — it will not homogenize your videos). No cloud/REST twin: the web copilot enforces this standard by hand. Details in `references/automation-and-local-dev.md` ("`vidfarm qa`").
1281
+ **On devcli there's a checker: `vidfarm qa <dir|composition.html>`.** Free, instant, local-only — a blocklist pass for everything above plus the font regime and safe zone, with a concrete fix per finding. **Run it on every video you produce.** It is feedback, not a gate (exit 0 even on findings, never runs automatically, `--strict` only if you want a CI failure) and a blocklist, not an allowlist (stylized/hand-made compositions pass untouched — it will not homogenize your videos). Every run — including a clean one — ends with a **`▶ NOW WATCH THE VIDEO`** block, because the check never rendered or saw the video and a green tick is not a review; do those steps before you tell anyone the video is done. No cloud/REST twin: the web copilot enforces this standard by hand. Details in `references/automation-and-local-dev.md` ("`vidfarm qa`").
1249
1282
 
1250
1283
  ### TikTok-native caption standard (position + font + background) — always adhere
1251
1284
 
1252
1285
  > Captions are also the *delivery system* for three of the four charges: the hook is read before any audio, the loop has to stay on screen, and the payoff number needs its own card. What the words should SAY is in `references/hooks-and-virality.md`; this section is how they must LOOK.
1253
1286
 
1254
- Short-form is watched on a phone, and the phone's UI eats the frame's edges. **Never pin on-screen text to the extreme top or bottom** — the top ~8% sits under the status bar / "Following · For You" tabs and the bottom ~15% under the username, caption text, music marquee, and action rail. Text there is literally clipped and reads as amateur. Three rules, applied to **every** caption/title/overlay you place or inherit:
1287
+ Short-form is watched on a phone, and the phone's UI eats the frame's edges. **Never pin on-screen text to the extreme top or bottom** — the top ~8% sits under the status bar / "Following · For You" tabs and the bottom ~15% under the username, caption text, music marquee, and action rail. Text there is literally clipped and reads as amateur. Four rules, applied to **every** caption/title/overlay you place or inherit:
1255
1288
 
1256
- - **Position → readable safe zone.** Keep the text box's vertical extent inside **~8%–85%** of canvas height (9:16). A lower-third subtitle lives around **y≈70%** (the `captions generate` default); a "me when…" / "POV:" meme line sits near the **top of the safe zone (y≈8%), not y:0**. Preserve the original top-vs-bottom anchoring — just pull it off the dead-zone edge, don't recenter everything. Also keep wide captions clear of the **right ~12%** action rail (a centered box at `x:10 width:80` is safe).
1257
- - **Font the composition regime.** Use the bundled display fonts only **Montserrat** (bold default, weight **700–900**), **TikTok Sans**, Abel, Source Code Pro, Yesteryear. Don't request a font the composition doesn't import (it silently falls back to a web-default sans, which is exactly the slop look). Sizes are in PIXELS of a 1080-wide frame: **~36–64px** reads well, never below ~28px and **never 0** (invisible). Keep captions to ~2 lines / ~5 words per line; `line_height` 0.95–1.15 for stacked display lines.
1289
+ - **Position → the safe zone first, then the EMPTIEST part of the frame.** Two constraints, in that order.
1290
+ - *Hard constraint:* the text box's vertical extent stays inside **~8%–85%** of canvas height (9:16), and wide captions stay clear of the **right ~12%** action rail (a centered box at `x:10 width:80` is safe).
1291
+ - *Judgement call, inside that band:* **put the words where the picture isn't.** `y≈70%` is the `captions generate` default because most footage puts its subject mid-frame — it is a default, not a law. Before you place text, **look at an actual frame** (`vidfarm stills ./work --at <t>`, free) and find the region with the least going on: open sky above a dashboard, a blank wall behind a talking head, an out-of-focus background, an empty tabletop. If nothing else in the video is competing for attention there — no subject, no motion, no product, no second text layer — that is where the caption belongs, even if it means **high-centre at y≈10–25%** instead of a lower third. A caption dropped over the busiest third of the frame (hands on a steering wheel, a face, the product) fights the shot and forces you to armour it with a plate; the same words parked in the sky are legible with no plate at all.
1292
+ - *When you're only rescuing an inherited caption* off a dead-zone edge, preserve its top-vs-bottom anchoring and just pull it inside the band — don't recentre a template you haven't re-read. When **you** are the one placing the text, place it deliberately.
1293
+ - **Size → scaled to the line, not maxed out.** Sizes are PIXELS of a 1080-wide frame: **~36–64px** reads well; below ~28px is unreadable on a phone and **0 is invisible**. Above ~64px is a *hook-word* size — one to three words, on purpose. The failure this catches: a full sentence set at display size runs edge-to-edge, wraps to three lines, and eats a third of the frame, so it has to be armoured with a full-width plate and there is nowhere left to put it. **If a line reaches the frame edges, the fix is a smaller size (or fewer words per cue), not a wider box.** Keep captions to ~2 lines / ~5 words per line; `line_height` 0.95–1.15 for stacked display lines.
1294
+ - **Font → the composition regime.** Use the bundled display fonts only — **Montserrat** (bold default, weight **700–900**), **TikTok Sans**, Abel, Source Code Pro, Yesteryear. Don't request a font the composition doesn't import (it silently falls back to a web-default sans, which is exactly the slop look).
1258
1295
  - **Background → one of exactly four valid treatments.** Any text you place uses one of these and nothing else:
1259
1296
 
1260
1297
  | # | Treatment | How to set it | When |
@@ -1264,10 +1301,19 @@ Short-form is watched on a phone, and the phone's UI eats the frame's edges. **N
1264
1301
  | 3 | **Highlight pill behind the ACTIVE word only** | `set_captions caption_style:"spotlight"` / `"karaoke"` (+ `caption_highlight_color`) | Hormozi/CapCut word-by-word. **The only legitimate "pill" in a video** — it tracks the spoken word, so it isn't a badge |
1265
1302
  | 4 | **Solid band that tightly hugs the text lines** (CapCut "text box") | `background_style:"highlight-solid"` (or `"highlight-translucent"`) + a `background` color | Guaranteed legibility over noisy footage |
1266
1303
 
1267
- **Pick between them by MEASURING the background behind the caption band, not by habit.** Dark-and-calm behind the band (luma < ~70, variation < ~42) light type, **no plate** (treatment 1/2 a plate there is a bright slab the design never asked for); bright-and-calm (luma > ~160) dark type, no plate; busy / mid-tone / moving colour → treatment 4, because nothing else stays readable. The active-word colour has to follow the same call a deep red that reads on a white plate is unreadable on near-black. **One treatment for the whole video**; styling that flips every few seconds reads as a bug. Procedure, thresholds and how to measure the *composited* value (not the source file): `regimes/short-form.QA_REGIME.md` "Caption styling is MEASURED off the background".
1304
+ **Move the text before you armour it.** The plate is the *last* resort, not the default: if the band you picked is busy, first try moving the caption into the calm/empty region the position rule points at a caption over open sky needs no background at all, and "no plate" is the cleaner, more native look every time you can afford it. Only when the whole frame is busy (or the text has to sit on the subject for meaning) do you reach for treatment 4.
1305
+
1306
+ **Then pick between them by MEASURING the background behind the caption band, not by habit.** Dark-and-calm behind the band (luma < ~70, variation < ~42) → light type, **no plate** (treatment 1/2 — a plate there is a bright slab the design never asked for); bright-and-calm (luma > ~160) → dark type, no plate; busy / mid-tone / moving colour → treatment 4, because nothing else stays readable. The active-word colour has to follow the same call — a deep red that reads on a white plate is unreadable on near-black. **One treatment for the whole video**; styling that flips every few seconds reads as a bug. Procedure, thresholds and how to measure the *composited* value (not the source file): `harnesses/short-form.HARNESS.md` → "Caption styling is MEASURED off the background".
1268
1307
 
1269
1308
  Treatment 4 is a **band, not a card**: it hugs the glyphs with minimal padding, corner radius ≤ ~8px, **no border, no drop shadow, no gradient, no blur**, and it wraps *one* text run — never a heading + subheading + URL stacked inside one rounded box. The moment it grows padding, a stroke, or a second element inside it, it has become a web card. Fix it. And the moment its radius goes fully round, it has become a **badge** — treatment 3 is the *only* capsule allowed, and only because it tracks the spoken word. A static "10 hrs / week" in a rounded pill is web furniture; the same words in treatment 1 or 2, bigger and heavier, are a beat.
1270
1309
 
1310
+ **Long narration → kinetic cues, never a wall of text.** A caption layer is a *page*, not a transcript. The moment a single static text run carries more than ~10–12 words — or sits on screen longer than ~4 seconds while the voice keeps going — it stops being a caption and becomes a paragraph the viewer has to read while also watching the video. Nobody does both; they scroll. Page it instead:
1311
+
1312
+ - **Transcribe and let the tool page it:** `vidfarm captions generate ./work --style word-pop` (or `spotlight` / `karaoke`) splits narration into ~3–5-word cues with real word-level timings, so one short phrase is on screen at a time and the active word tracks the voice. Web copilot twin: the `/primitives/audio/captions` job → `set_captions` (see "Animated captions" below). `--max-words-per-cue` tightens it further.
1313
+ - **The cue count is the readability dial.** Short cues that change with the speech read as *momentum*; one long block reads as homework. Kinetic word-by-word also lets the type be **smaller** (the eye is led to the moving word instead of having to scan a wall), which frees up frame space and usually removes the need for a plate.
1314
+ - **Static text is for the beats that deserve their own moment** — a hook line, a payoff number, a title card. Those are short by nature. Anything spoken should be a caption run, not a static block.
1315
+ - **Exception: verbatim UGC/testimonial captions** stay one plain line at a time (see `harnesses/ugc-testimonial.HARNESS.md`) — the kinetic VFX look is the "made by a marketing team" tell there. Paging still applies; the animation preset doesn't.
1316
+
1271
1317
  **A common trap: decomposed templates mirror the source's caption placement**, so a forked meme can arrive with its caption pinned at `top:0` in a non-regime font — and a re-theme prompt ("make this for my tutoring service") is exactly where an agent starts inventing landing-page CTAs and benefit chips because the *subject* is a SaaS product. **Fix to the standard, don't inherit it, and don't import the website's design language into the video.** When placing text yourself (`set_captions`, `set_layer_text`, `set_layer_style`, `add_layer`, devcli `place`/`captions`), set `y` / `font_family` / `font_weight` / `background_style` to the standard from the start.
1272
1318
 
1273
1319
  > Local devcli renders enforce part of this automatically: `renderCompositionLocally` runs `normalizeTikTokCaptionLayout` (src/devcli/composition-edit.ts) on every production, clamping caption/text layers into the 8%–85% safe zone and coercing off-regime primary fonts to Montserrat. It only fixes position and font family — it will happily render your Bootstrap card. Get it right in the composition so the editor preview, the local render, and any cloud render match.
@@ -1372,7 +1418,7 @@ Most agent-made videos don't fail on polish. They fail on **structure**: no hook
1372
1418
 
1373
1419
  This is the harness that fixes it. It is not a style — it's the load-bearing anatomy of anything that travels on TikTok/Reels/Shorts, distilled from grading hundreds of hooks against real funnels. **Run it on one-off videos and on batches alike.** It costs no credits, adds no render time, and it is the single largest quality delta available in this product.
1374
1420
 
1375
- The checkable form of this document is the bundled `hooks` regime (`vidfarm regime show hooks`); this file is the craft behind it.
1421
+ The checkable form of this document is the bundled `hooks` harness (`vidfarm harness show hooks`); this file is the craft behind it.
1376
1422
 
1377
1423
  ---
1378
1424
 
@@ -1385,10 +1431,67 @@ The reason agent videos come out structureless is that the timeline is the fun p
1385
1431
  3. **Name the payoff.** What is on screen at that moment, and why does it satisfy the promise?
1386
1432
  4. **Write the bait.** The final-beat ask, in the video and in the post caption.
1387
1433
  5. **Only now build the timeline** — and place the hook text at `start:0` so it's on screen at frame 0 (which is also the thumbnail).
1388
- 6. **Verify the frame and the structure:** `vidfarm stills ./work --at 0` (look at the actual poster) and `vidfarm qa ./work --regime hooks` (machine checks + the judgment checklist).
1434
+ 6. **Verify the frame and the structure:** `vidfarm stills ./work --at 0` (look at the actual poster) and `vidfarm qa ./work --harness hooks` (machine checks + the judgment checklist).
1389
1435
 
1390
1436
  Steps 1–4 are cheap, reversible, and where the entire outcome is decided. Steps 5–6 are where agents want to start.
1391
1437
 
1438
+ 7. **Cut it.** Nothing ships at its first length — see the next section. Assume your first assembly is 30–50% too long and go find the seconds.
1439
+
1440
+ ---
1441
+
1442
+ ## Density — every second must earn its place, and most don't
1443
+
1444
+ **A viewer's thumb is a hard time limit that resets every second.** They are not "watching your video"; they are re-deciding to stay, ~24 times a second, against an infinite feed of alternatives. A second that carries nothing is not neutral — it is a free exit. This is why the same script cut to 22s outperforms its own 41s version with better footage: fewer exit ramps.
1445
+
1446
+ Agents are structurally bad at this. A model writes a video the way it writes prose — with connective tissue, restatement, a wind-up before the point, a tidy conclusion — and every one of those habits is a hole in the retention curve. **You must cut against your own instinct, and you must cut more than feels right.**
1447
+
1448
+ ### The deletion test — the only test that matters
1449
+
1450
+ For every beat, ask: **delete it. Does the video still make sense, and does the payoff still land?** If yes, it stays deleted. Not "trimmed" — deleted. Run this on every scene, every sentence, and every caption before you render, and be honest: the beat you're defending because it took work to make is exactly the one this test exists to kill.
1451
+
1452
+ Second filter for whatever survives: **which of the four charges does this beat serve — hook, loop, payoff, or bait?** A beat that serves none is fluff wearing a costume. "It gives context" is not a charge. "It looks nice" is not a charge.
1453
+
1454
+ ### Cut on sight — the standard fluff, in the order it usually appears
1455
+
1456
+ - **Any intro.** Logo sting, title card, brand animation, "welcome back", a beat of black. The video starts at the claim. Frame 0 is the hook (and the thumbnail).
1457
+ - **The wind-up before the point.** "So I wanted to talk about…", "Here's the thing…", "Let me explain…", "In this video I'm going to show you…". Delete the sentence; the next one was the real opening.
1458
+ - **Context before the claim.** Context is beat 2 at the earliest, and usually one clause, not a scene.
1459
+ - **Restatement.** Saying the same thing a second way "so it's clear." It was clear. If it wasn't, fix the first version.
1460
+ - **Dead air in the narration.** Breaths, "um", and any inter-sentence gap over ~0.35s. This alone routinely takes 15–20% off a TTS or talking-head cut.
1461
+ - **Real-time process.** Nobody watches the upload bar. Speed-ramp it, jump-cut it, or show the before and the after and skip the middle.
1462
+ - **Establishing shots.** They know what an office/kitchen/laptop looks like. Open inside the action.
1463
+ - **Reading what's already on screen.** Voice and text should split the work, not duplicate it (the same rule as captions-vs-display-text).
1464
+ - **The tail.** "Thanks for watching", a logo card, an end screen, or footage that keeps rolling after the last word. The bait is the last beat; then it **ends**, hard, on the frame that loops best.
1465
+ - **Filler motion.** A slow pan or Ken Burns that exists because the clip was too short for its slot. Shorten the slot instead.
1466
+
1467
+ ### Density is not speed, and this is where over-correcting ruins videos
1468
+
1469
+ Cutting fluff means **removing beats that carry nothing**, never rushing the beats that carry everything. Three things are load-bearing and must keep their seconds:
1470
+
1471
+ - **The comedic beat.** The held pause before a punchline IS the joke. Cutting it saves 0.6s and costs the video.
1472
+ - **The payoff.** It plays, full frame, uninterrupted — ≥5s if that's what it takes. Summarising the payoff to save time is the most expensive cut available.
1473
+ - **A caption's readability.** A cue nobody can finish reading is worse than no cue. If tightening the edit makes text unreadable, cut *words*, not the time they're on screen.
1474
+
1475
+ The target is **information per second**, not seconds. A dense 45s video beats a hollow 20s one; both lose to the same 45s cut to 30s with nothing lost.
1476
+
1477
+ ### Length is an output, not a plan
1478
+
1479
+ Don't decide "make it 60 seconds" and then fill 60 seconds — filling is where every one of the fluff patterns above comes from. Build the four charges, cut to the deletion test, and **the length is whatever's left.** If the payoff lands at 0:25, the video ends around 0:27. A brief that dictates a duration is a brief that ordered fluff.
1480
+
1481
+ ### How to actually cut it, in Vidfarm
1482
+
1483
+ | Move | devcli | Web copilot |
1484
+ |---|---|---|
1485
+ | Find the dead air | `vidfarm qa ./work` (flags gaps ≥2.5s with nothing on screen, and a tail that keeps rolling after the last word) + read the word timings from `vidfarm captions generate` / `stt` | read `video_context`'s timestamped segments and look for the gaps between them |
1486
+ | Trim one clip's edge | `vidfarm trim ./work --layer <k> --edge start --to-time <sec>` | `editor_action trim_layer` |
1487
+ | Close the hole you just made | `vidfarm ripple ./work --at <sec> --delta -<sec>` (negative = close time, shifts everything downstream) | `editor_action ripple_edit` |
1488
+ | Drop a whole beat | `vidfarm retime`/`remove` the layers, then `ripple` the gap closed | `remove_layer` + `ripple_edit` |
1489
+ | Re-time captions after cutting | re-run `vidfarm captions generate` against the new audio — never hand-shift cues | the `/primitives/audio/captions` job → `set_captions` |
1490
+
1491
+ **Always ripple the gap closed.** A cut that leaves a hole is not a cut; it converts fluff into dead air, which is worse — the viewer now stares at a frozen frame instead of a boring one.
1492
+
1493
+ **Cheap habit that pays every time:** shave the first ~0.5–1s off every sourced clip and the last ~0.5s. People start recording before the action and stop after it, so a montage of raws is carrying a second of nothing per clip by default.
1494
+
1392
1495
  ---
1393
1496
 
1394
1497
  ## Charge 1 — THE HOOK (first 3 seconds)
@@ -1539,10 +1642,11 @@ A video with replies gets shown again; a video with none dies at its first audie
1539
1642
  - **The named omission** — *"I left one thing out on purpose. Ask me."* Converts what you couldn't fit into the reason to reply.
1540
1643
  - **The soft disagree** — a true concession people want to argue with. Not ragebait if you actually believe it.
1541
1644
  - **The specific question** — concrete always beats "thoughts?".
1645
+ - **The keyword comment** — *"Comment CLIPPER and I'll send you the breakdown."* Standard, allowed, and it works: the keyword is the reply the algorithm counts, and the word itself doubles as a filter. Keep the keyword topical (name the thing, not the money), promise something you actually hand over, and say what they get. What makes this beat go wrong is the *promise*, not the mechanic.
1542
1646
 
1543
- **⚠️ Bait is the highest-compliance-risk beat in the video.** For money/health/appearance topics the obvious bait is the illegal one:
1647
+ **⚠️ Bait is the highest-compliance-risk beat in the video.** For money/health/appearance topics the risk is what you promise in exchange for the reply:
1544
1648
 
1545
- - ❌ *"Comment MONEY and I'll send you the link"* — the most scam-coded line available, and a DM funnel.
1649
+ - ❌ *"Comment MONEY and I'll show you how to make $10k/mo"* — the keyword is fine; the earnings claim attached to it is what's scam-coded and enforceable. Same line with a topical keyword and a deliverable ("Comment CLIPPER for the template") is fine.
1546
1650
  - ❌ *"How much do you want to make a month?"* — invites an earnings-claim thread you now own.
1547
1651
  - ❌ *"Follow for part two"* — breaks the standalone rule.
1548
1652
  - ❌ Fake controversy, ragebait, or a deliberate error to farm corrections. Works once, costs the positioning permanently.
@@ -1597,12 +1701,12 @@ A video with replies gets shown again; a video with none dies at its first audie
1597
1701
  | Check what the source template's hook actually was | `editor_context` → `viral_dna.hook` / `retention` / `payoff` / `emotional_punch` | `.harness/context.json`, `video-context.json` |
1598
1702
  | Place the hook at frame 0 | `add_layer` / `set_captions` with `start:0` | `vidfarm set-text ./work --layer hook --text "…"` |
1599
1703
  | Look at the poster frame | ask the user to scrub to 0 | `vidfarm stills ./work --at 0` |
1600
- | Grade the structure | by hand, against this file | `vidfarm qa ./work --regime hooks` |
1601
- | Bulk hook test | hand off to a local agent | `recipes/bulk-scripting-with-a-regime.md` |
1704
+ | Grade the structure | by hand, against this file | `vidfarm qa ./work --harness hooks` |
1705
+ | Bulk hook test | hand off to a local agent | `recipes/bulk-scripting-with-a-harness.md` |
1602
1706
 
1603
1707
  **Re-theming a decomposed template?** `viral_dna` already names the source's hook, retention device, and payoff — that structure is *why the template worked*. Rebuild each charge for the new subject; don't drop the loop because the new topic feels self-explanatory. Flattening a template's loop into a product statement is the single most common way a re-theme kills a format.
1604
1708
 
1605
- **The checkable version of everything above:** `vidfarm regime show hooks` — the twelve-item pre-flight checklist is the part you answer honestly on every video, and two items carry most of the weight: *situation, not label* (predicts cold-start survival before you write a word) and *unguessable* (the only item a hook can fail while passing every other one, which is why it ships).
1709
+ **The checkable version of everything above:** `vidfarm harness show hooks` — the twelve-item pre-flight checklist is the part you answer honestly on every video, and two items carry most of the weight: *situation, not label* (predicts cold-start survival before you write a word) and *unguessable* (the only item a hook can fail while passing every other one, which is why it ships).
1606
1710
 
1607
1711
  ## Reviewing a render — look at the whole video, and never trust one frame
1608
1712
 
@@ -1643,6 +1747,7 @@ ffmpeg -y -pattern_type glob -i "qa/f*.png" \
1643
1747
  Then answer these, out loud, in your report:
1644
1748
 
1645
1749
  - **Balance.** Is weight distributed across the frame, or is every scene top-anchored with an empty band underneath? Does the composition use the canvas, or does it use the top third of the canvas and leave the rest as dead area? A sheet of twelve frames makes a recurring dead zone obvious; one frame at a time never will.
1750
+ - **Fluff, named out loud.** Which beats would you cut? Answer with specific timestamps, not "it's tight". Every tile has to justify its seconds: a frame that repeats the previous one, a scene the video would survive losing, an intro, a tail after the last word, a hold that's just waiting. **Assume 30–50% of the first assembly can go** and name what you'd remove — "nothing to cut" on a first pass is almost always a review that didn't look. Then cut it and `ripple` the hole closed (craft: `references/hooks-and-virality.md` → "Density"; the mechanical half is `vidfarm qa`'s `dead-air` / `dead-tail` / `slow-scene`).
1646
1751
  - **Spacing and breathing room.** Are margins consistent scene to scene? Does one beat have generous air and the next one crowd the safe zone? Uneven padding across scenes is the single loudest "assembled by a machine" tell, and it's invisible while you're inside any one scene.
1647
1752
  - **Typographic continuity.** One type system, or three? Headline sizes should belong to a small set (two, maybe three), not be individually chosen per scene. Same for weight, case, and colour. If scene 2's headline is 64px and scene 5's is 41px for no dramatic reason, that's drift, not design.
1648
1753
  - **Colour and style coherence.** One accent colour, one background treatment, one illustration style. Assets generated or sourced at different moments drift — a flat-vector sticker next to a photographic cutout next to a gradient panel reads as three videos spliced together.
@@ -1740,7 +1845,7 @@ ffmpeg -i final.mp4 -ss 3.1 -t 1.4 -af "volumedetect" -f null - # a span where
1740
1845
  When you hand back a render, say what you **measured** and what you **judged**, separately:
1741
1846
 
1742
1847
  - Machine-settled: `vidfarm qa ./work` findings, `vidfarm lint`, durations, peak dBFS, frame-difference checks.
1743
- - Human-judgment: the holistic pass above — balance, spacing, type continuity, colour coherence, pacing, joins — plus the regime's `- [ ]` review items.
1848
+ - Human-judgment: the holistic pass above — balance, spacing, type continuity, colour coherence, pacing, joins — plus the harness's `- [ ]` review items.
1744
1849
 
1745
1850
  **Never report a clean pass on the half you didn't actually look at.** A confident "verified, looks good" over an unreviewed video is worse than no review, because it spends the director's trust on nothing — and per the base rate at the top of this file, it is usually wrong.
1746
1851
 
@@ -1971,38 +2076,62 @@ Send a stable `tracer` on export so retries are traceable and filterable in job
1971
2076
  | | **One-time video** | **Bulk / scripting mode** |
1972
2077
  |---|---|---|
1973
2078
  | The deliverable | One MP4 you both look at | A loop that produces N videos nobody watches frame-by-frame |
1974
- | Quality control | Your eyes on the render | **A `QA_REGIME.md`** — the batch's written standard |
2079
+ | Quality control | Your eyes on the render | **A `HARNESS.md`** — the batch's written standard |
1975
2080
  | What you optimize | This video | The *variant axis* (one thing changes; everything else is held) |
1976
2081
  | Cost posture | Per-video decisions are fine | Per-video AI spend × N — reuse assets, prefer clip pools |
1977
2082
 
1978
- A director who says "make me a video about X" usually wants the first. A director who says "I need to post daily" / "make 20 variants" / "test hooks" wants the second and often doesn't know it has a name. **Offer the upgrade explicitly:** *"Want this as one video, or should we set it up as a repeatable batch? Batches get a QA_REGIME.md so variant #37 is as good as #1."* Don't silently build a one-off when they asked for volume, and don't drag someone into a scripting harness when they wanted one clip.
2083
+ A director who says "make me a video about X" usually wants the first. A director who says "I need to post daily" / "make 20 variants" / "test hooks" wants the second and often doesn't know it has a name. **Offer the upgrade explicitly:** *"Want this as one video, or should we set it up as a repeatable batch? Batches get a HARNESS.md so variant #37 is as good as #1."* Don't silently build a one-off when they asked for volume, and don't drag someone into a scripting harness when they wanted one clip.
2084
+
2085
+ ### `HARNESS.md` — the reusable AI harness for a format
1979
2086
 
1980
- ### `QA_REGIME.md` — the quality contract for a batch
2087
+ `vidfarm qa`'s built-in rules are **universal** (no HTML slop, the font regime, the thumbnail frame) — the same for everyone, so they live in code. A harness is the opposite: it's what makes **this** director's **this** format good their audience, hook shape, banned vocabulary, pacing, compliance line, and the DNA of the template it came from. It can't be hard-coded, so it lives next to the work as Markdown they own and version.
1981
2088
 
1982
- `vidfarm qa`'s built-in rules are **universal** (no HTML slop, the font regime, the thumbnail frame) the same for everyone, so they live in code. A regime is the opposite: it's what makes **this** director's **this** format good — their audience, hook shape, banned vocabulary, pacing, compliance line. It can't be hard-coded, so it lives next to the work as Markdown they own and version.
2089
+ **It exists because bulk output loses its human reviewer.** One video gets eyes on every frame; fifty generated in a loop do not. The harness is what the loop grades against.
1983
2090
 
1984
- **It exists because bulk output loses its human reviewer.** One video gets eyes on every frame; fifty generated in a loop do not. The regime is what the loop grades against.
2091
+ **Three director phrasings, one artifact:**
2092
+
2093
+ | They say | You run |
2094
+ |---|---|
2095
+ | "create me a harness" | `vidfarm harness init <base> --out ./work/HARNESS.md`, then edit it with them |
2096
+ | "update the harness for this format" | open the file, add the rule **with its reason**, re-run `vidfarm qa` |
2097
+ | "give me the harness for this template_id" | `vidfarm harness derive <templateId\|forkId>` — the **decomposition**, as a harness |
1985
2098
 
1986
2099
  ```bash
1987
- vidfarm regime list # the bundled starting points
1988
- vidfarm regime init short-form --out ./work/QA_REGIME.md # copy, then EDIT it
1989
- vidfarm qa ./work # auto-picks up ./work/QA_REGIME.md
1990
- vidfarm qa ./work --regime hooks --regime ./brand/HOUSE.md # built-in + your own file — they STACK
2100
+ vidfarm harness list # the bundled starting points
2101
+ vidfarm harness init short-form --out ./work/HARNESS.md # copy, then EDIT it
2102
+ vidfarm harness derive <forkId> --out ./work/HARNESS.md # a decomposed template → a harness
2103
+ vidfarm harness show ./work/HARNESS.md --dna visual # ONE strand, not the whole doc
2104
+ vidfarm qa ./work # auto-picks up ./work/HARNESS.md
2105
+ vidfarm qa ./work --harness hooks --harness ./brand/HOUSE.md # built-in + your own file — they STACK
1991
2106
  ```
1992
2107
 
1993
- Bundled bases (`vidfarm regime list`, files under `.agents/skills/vidfarm/regimes/`): **`short-form`** (the default the four charges hook/loop/payoff/bait + the standalone rule), **`hooks`** (hook-variant batches: chunk-1 legibility, the unguessable test, the anti-patterns that only show up at volume), **`ugc-testimonial`**, **`explainer`**, **`product-demo`**. Each is a *starting point to edit*, never a house style to conform to — the parts that matter most are the parts the director adds. A regime can also be any file anywhere: `--regime ./campaigns/q3/RULES.md` is fully supported, and `VIDFARM_QA_REGIME=./work/QA_REGIME.md` sets a default for a whole run.
2108
+ **A harness mirrors the template JSON's DNA vocabulary.** Every `## DNA` heading is indexed under the same key the decompose pass uses, so a derived harness and a hand-written one read the same:
2109
+
2110
+ | Strand | What lives there | Decompose source |
2111
+ |---|---|---|
2112
+ | **Viral DNA** | hook, retention mechanic, payoff, core emotion, contrast | `video-context.json` → `viral_dna` |
2113
+ | **Visual DNA** | cut rhythm, energy curve, caption style/placement, b-roll, transitions | `editor-harness.json` → `pacing` / `typography` / `broll` |
2114
+ | **Structural DNA** | the beats, their roles, which are load-bearing | `editor-harness.json` → `scenes`, `scene-annotations.json` |
2115
+ | **Audio DNA** | voiceover, bed, SFX, comedic timing, intonation | `editor-harness.json` → `audio` / `emotional` |
2116
+ | **Build DNA** | which paintbrush per beat, the free-tier path | `replication-harness.json` |
2117
+
2118
+ `harness derive` writes what the decompose pass actually recorded and marks the rest `unknown` — it never invents a strand to look complete. Treat its output as a **first draft**: the model watched the video, it didn't talk to the customer.
2119
+
2120
+ > Don't confuse `HARNESS.md` with the `.harness/` directory `vidfarm pull` writes. That directory is machine-generated context (`context.json`, `agent-guide.md`), regenerated on every pull — never hand-edit it. `HARNESS.md` is the one the director owns.
1994
2121
 
1995
- **The format is two halves, and the split is deliberate:** a front-matter `checks:` block the CLI settles deterministically (duration, aspect, `hook_words_max`, `forbid_text`, `first_frame_text`, full key list in `regimes/README.md`), and every `- [ ]` checkbox in the body, which comes back as a **review item for you to answer**. "Is the withheld answer one the viewer can't supply themselves?" is a judgment call; a linter claiming to settle it would be lying. **Answer the review items honestly in your report** the CLI prints them precisely because it can't.
2122
+ Bundled bases (`vidfarm harness list`, files under `.agents/skills/vidfarm/harnesses/`): **`short-form`** (the default the four charges hook/loop/payoff/bait + the standalone rule), **`hooks`** (hook-variant batches: chunk-1 legibility, the unguessable test, the anti-patterns that only show up at volume), **`ugc-testimonial`**, **`explainer`**, **`product-demo`**. Each is a *starting point to edit*, never a house style to conform to the parts that matter most are the parts the director adds. A harness can also be any file anywhere: `--harness ./campaigns/q3/RULES.md` is fully supported, and `VIDFARM_HARNESS=./work/HARNESS.md` sets a default for a whole run.
1996
2123
 
1997
- **Build on it.** When you learn something from a batch ("the label-framed hooks all died"), write it into the regime as a new rule or checklist line. That is the artifact that compounds across runs; the composition files don't.
2124
+ **The format is two halves, and the split is deliberate:** a front-matter `checks:` block the CLI settles deterministically (duration, aspect, `hook_words_max`, `forbid_text`, `first_frame_text`, … — full key list in `harnesses/README.md`), and every `- [ ]` checkbox in the body, which comes back as a **review item for you to answer**. "Is the withheld answer one the viewer can't supply themselves?" is a judgment call; a linter claiming to settle it would be lying. **Answer the review items honestly in your report** — the CLI prints them precisely because it can't.
1998
2125
 
1999
- ### The bulk loop, with the regime in it
2126
+ **Build on it.** When you learn something from a batch ("the label-framed hooks all died"), write it into the harness as a new rule or checklist line. That is the artifact that compounds across runs; the composition files don't.
2127
+
2128
+ ### The bulk loop, with the harness in it
2000
2129
 
2001
2130
  ```bash
2002
- vidfarm regime init hooks --out ./work/QA_REGIME.md # once, then edit for this account
2131
+ vidfarm harness init hooks --out ./work/HARNESS.md # once, then edit for this account
2003
2132
  for VARIANT in "${VARIANTS[@]}"; do
2004
2133
  vidfarm set-text ./work --layer hook --text "$VARIANT"
2005
- vidfarm qa ./work --json > "qa/$SLUG.json" # regime auto-discovered from ./work
2134
+ vidfarm qa ./work --json > "qa/$SLUG.json" # harness auto-discovered from ./work
2006
2135
  jq -e '.ok' "qa/$SLUG.json" >/dev/null || continue # YOUR gate, in YOUR script
2007
2136
  vidfarm render "$FORK_ID" --dir ./work --out "renders/$SLUG.mp4"
2008
2137
  done
@@ -2154,14 +2283,14 @@ The licensed harness also carries the **generative build workflow** guidance (ch
2154
2283
  | `vidfarm generate <image\|video> --prompt "…"` | `POST /api/v1/primitives/{images,videos}/generate` (polls job) | generate AI media → finished URL |
2155
2284
  | `vidfarm inpaint <image> --mask <png> --prompt "…" [--region "label=…"] [--ref …] [--out <f>]` | `POST /api/v1/primitives/images/inpaint` (polls job) | masked image EDIT — replace ONLY the transparent-mask region, keep everything else (devcli twin of the /inpaint page) |
2156
2285
  | `vidfarm create-overlay "<subject>" [--key-color #00FF00] [--aspect-ratio 1:1] [--place <dir>] [--out <f>]` | `POST /api/v1/primitives/images/create-overlay` (polls job) | **Vox-style** transparent OVERLAY — AI image on a forced key-color background, chroma-keyed out in one job → ready-to-composite transparent PNG |
2157
- | `vidfarm remove-greenscreen <image\|video> [--preset green\|blue\|white\|black\|digital-green\|magenta] [--key-color #00FF00] [--tolerance 0.3] [--local] [--gif] [--out <f>]` | `POST /api/v1/primitives/remove-greenscreen` (polls job) | chroma-key a FLAT solid background → transparent PNG/WebP (image) or WebM/VP9-alpha (video); auto-detects media kind. `--local` runs it FREE in-process (sharp/ffmpeg, no wallet); default cloud is billed at real compute × 1.2. **`--gif` writes a transparent GIF instead** (ANIMATED for a clip; `--gif-fps`/`--gif-width`/`--gif-alpha`) — local-only, 1-bit alpha, for GIF-only sticker surfaces; prefer PNG/WebP/WebM for compositing. Aliases: `greenscreen`, `remove-background-greenscreen`. |
2286
+ | `vidfarm remove-greenscreen <image\|video> [--preset green\|blue\|white\|black\|digital-green\|magenta] [--key-color #00FF00] [--tolerance 0.3] [--local] [--gif] [--out <f>]` | `POST /api/v1/primitives/remove-greenscreen` (polls job) | chroma-key a FLAT solid background → transparent PNG/WebP (image) or WebM/VP9-alpha (video); auto-detects media kind. `--local` runs it FREE in-process (sharp/ffmpeg, no wallet); default cloud is billed at real compute × 1.2. **`--gif` writes a transparent GIF instead** (ANIMATED for a clip; `--gif-fps`/`--gif-width`/`--gif-alpha`) — local-only, 1-bit alpha, for GIF-only sticker surfaces; prefer PNG/WebP/WebM for compositing. **`--smart`** (IMAGE + local only) swaps the flat chromakey for the connectivity keyer `cutout`/`sticker-pack` use, so plate-colored detail inside the subject and hollow/outline art survive. Aliases: `greenscreen`, `remove-background-greenscreen`. |
2158
2287
  | `vidfarm dedupe <video\|image\|url> [--preset light\|standard\|strong] [--variants N] [--seed <s>] [--zoom/--rotate/--skew/--speed/--saturation/--hue/--noise/--flip] [--local\|--cloud] [--out <f>\|--out-dir <d>]` | **local, free, ffmpeg-only** by default (no job); `--cloud` = `POST /api/v1/primitives/media/dedupe` (polls job) | **DEDUPLICATION — the publish-safety pass.** Makes a finished render read as a NEW upload to a platform's duplicate-content detector, invisibly to a viewer. Default preset `standard` = skew 2%, zoom 3%, rotate 2°, speed +2%, saturation +4%, plus contrast/brightness/hue/grain, a container-metadata strip and a per-variant CRF walk. **Runs on the EXPORTED file — never re-render for this.** `--variants N` mints N copies that differ from the original AND from each other (jittered magnitudes, alternating signs), one per account/posting slot; `--seed` makes a batch reproducible. A rotate forces a bigger centre-crop to hide the black corners (~6.7% on a tall frame at 2°) and says so — pass `--rotate 0` when framing matters more. `--flip` is the strongest single knob but visibly reverses on-screen text. **Ask the operator whether they want this BEFORE publishing or bulk-producing.** Aliases: `dedup`, `deduplicate`, `uniquify`. |
2159
- | `vidfarm cutout <image\|url> [--generate "<prompt>"] [--preset green] [--pad <px>] [--alpha-threshold <n>] [--no-trim] [--output-format png\|webp] [--out <f>]` | **local, free, ffmpeg-only** (no job) — key + `alphaextract`/`cropdetect` trim | **The transparent explainer-STICKER maker.** Keys out the flat plate **and then shrinks the canvas to the cutout's true min width/height** (a 1024² mostly-empty plate → a snug sticker whose pixel size IS the subject) so you can scale/position it precisely. `--generate` AI-generates the graphic first on a matching chroma plate (that step is the billed image primitive), then keys+trims in one shot; without it, keys+trims a file/url you already have. **IMAGE-only** (a moving subject has no single bounding box — key a clip with `remove-greenscreen`). Prefer this over `create-overlay` locally: same idea, but free and auto-trimmed. `--pad` keeps transparent breathing room; `--json` reports final `width`/`height`/`area_reduced_pct`, plus `hole_pct`/`hollow` the "the key ate the fill" check (outline-only art keys into a rim around a transparent hole; `--generate` prompts against it automatically, and the console prints a `Hollow:` warning with the fix). Alias: `sticker`. See recipe `cutout-graphics-for-explainers.md`. |
2288
+ | `vidfarm cutout <image\|url> [--generate "<prompt>"] [--preset green] [--pad <px>] [--alpha-threshold <n>] [--no-trim] [--output-format png\|webp] [--out <f>]` | **local, free, ffmpeg-only** (no job) — key + `alphaextract`/`cropdetect` trim | **The transparent explainer-STICKER maker.** Keys out the flat plate **and then shrinks the canvas to the cutout's true min width/height** (a 1024² mostly-empty plate → a snug sticker whose pixel size IS the subject) so you can scale/position it precisely. `--generate` AI-generates the graphic first on a matching chroma plate (that step is the billed image primitive), then keys+trims in one shot; without it, keys+trims a file/url you already have. **IMAGE-only** (a moving subject has no single bounding box — key a clip with `remove-greenscreen`). Prefer this over `create-overlay` locally: same idea, but free and auto-trimmed. Keys by CONNECTIVITY by default (`--key-mode smart`): the plate is flood-filled in from the frame edge, so plate-colored detail inside the subject and hollow/outline art survive, and the plate is un-mixed out of each edge pixel individually instead of by a global `despill`. `--key-mode flat` is the old plain chromakey. `--pad` keeps transparent breathing room; `--json` reports final `width`/`height`/`area_reduced_pct`, `key_mode`, `protected_px` (plate-colored pixels kept inside the art), plus `hole_pct`/`hollow` (under the smart keyer an enclosed hole is usually real art, so it warns only). Alias: `sticker`. See recipe `cutout-graphics-for-explainers.md`. |
2160
2289
  | `vidfarm mask <image\|url> [--crop x,y,w,h] [--flat <hex>] [--pad <px>] [--alpha-threshold <n>] [--no-trim] [--output-format png\|webp] [--keep-region <f>] [--out <f>]` | **local, free** (no job) — ffmpeg crop + ONNX matting (or ffmpeg chroma-key) + `cropdetect` trim | **Lift an illustration OUT of an image you already have** (infographic / poster / marketing graphic / brand sheet / screenshot) → snug transparent PNG, the same reusable explainer sticker `cutout` makes but with **$0 and zero AI generation** — the cost-saving move whenever source art exists. `--crop x,y,w,h` (pixels **or** %) isolates ONE element from a multi-illustration source before masking (re-run with different rects to grab each). Background removed by **local ONNX matting** (any/busy background) by default, or **`--flat <hexcolor>`** chroma-keys a solid fill for crisper edges (an infographic's cream/white paper); then trims to the subject's true min width/height. **IMAGE-only** (matte a clip with `remove-background`). Aliases: `isolate`, `extract`. See recipe `cutout-graphics-for-explainers.md` → "Mask from an image you already have". |
2161
- | `vidfarm sticker-pack [sheet\|url] [--generate "<theme>"] [--items "a,b,c"] [--count <n>] [--dry-run] [--gap <pct>] [--min-area <pct>] [--output-format png\|webp\|gif] [--out-dir <d>]` | **local, free, ffmpeg-only** (no job; only `--generate` bills, ONCE for the whole set) — key + alpha-channel segmentation + per-item trim | **The STICKER-PACK maker — the answer whenever a director asks for "a sticker pack" / prop set / icon set.** A pack is ONE greenscreen sheet holding every item, keyed once and then masked apart: 1/N the cost of N `cutout` calls, and the only way a cast stays on-style. Finds each item **automatically** by segmenting the keyed sheet's alpha into connected islands — no hand-measured `--crop` rects — and writes one snug transparent file per item (named from `--items`, reading order) plus a `stickers.json` manifest. `--dry-run` prints the detected boxes first; `--gap` merges (lower) or splits (raise) items that came out joined/broken; items have **no maximum size** — a full-frame landscape/backdrop is as valid a sticker as a 3% icon. **Plate color is chosen for you:** when generating it reads the subject and moves the plate off any hue the art uses (green magenta blue black white — a pack of leaves/frogs/money on GREEN would key holes through the art), and when splitting an existing sheet it DETECTS the plate from the sheet's four corners, so a red/purple sheet handed back from a web tool just works. Pin it with `--key-color`/`--preset`, or `--no-auto-key` for plain green. **The ART is made key-safe too:** the generation prompt is auto-appended with "closed, solidly filled shapes, no outline-only/hollow art, nothing in the plate hue or a near-shade, fully opaque, no glow/translucency" the fix for stickers that come back as a rim around a transparent hole and after keying each item reports `holes`/`hole_pct`/`hollow` (console `⚠ N% hollow` at ≥20%, plus `--json` and `stickers.json`). It **warns, never blocks** (a ring/frame/donut reads identically); re-generate with the fill clause, or lift that one item with `vidfarm mask --crop …`. `--output-format gif` emits 1-bit-alpha GIFs for GIF-only surfaces. IMAGE-only. Aliases: `stickers`, `sticker-sheet`. See recipe `cutout-graphics-for-explainers.md` → "A sticker pack". |
2290
+ | `vidfarm sticker-pack [sheet\|url] [--generate "<theme>"] [--items "a,b,c"] [--count <n>] [--dry-run] [--gap <pct>] [--min-area <pct>] [--output-format png\|webp\|gif] [--out-dir <d>]` | **local, free, ffmpeg-only** (no job; only `--generate` bills, ONCE for the whole set) — key + alpha-channel segmentation + per-item trim | **The STICKER-PACK maker — the answer whenever a director asks for "a sticker pack" / prop set / icon set.** A pack is ONE greenscreen sheet holding every item, keyed once and then masked apart: 1/N the cost of N `cutout` calls, and the only way a cast stays on-style. Finds each item **automatically** by segmenting the keyed sheet's alpha into connected islands — no hand-measured `--crop` rects — and writes one snug transparent file per item (named from `--items`, reading order) plus a `stickers.json` manifest. `--dry-run` prints the detected boxes first; `--gap` merges (lower) or splits (raise) items that came out joined/broken; items have **no maximum size** — a full-frame landscape/backdrop is as valid a sticker as a 3% icon. **The key is CONNECTIVITY-based (`--key-mode smart`, the default):** the plate is flood-filled inward from the sheet's edge and only background that REACHES the edge is deleted, so plate-colored detail inside an item, and an outline shape whose interior is bare plate, both survive; edge pixels get the plate un-mixed out of them individually (real alpha math, better than a global `despill`). `--key-mode flat` is the old plain chromakey the simple fallback and the way to match a cloud render exactly. **ONE PLATE PER STICKER (`--sheet-mode zoned`):** generates a grid of solid color PANELS, one item per panel, each panel's plate picked against that item (green frog on magenta beside a pink flower on green), keyed panel-by-panel with the color read back off that panel's own corners — item art may then use any palette including other panels' colors, and names map to PANELS instead of reading order (`panel`/`plate` land in `stickers.json`). `--sheet-mode auto` (default) zones a generation of 2+ named items; `--sheet-mode flat` forces the classic one-color sheet for a weaker model, and if a model ignores the grid anyway the keyer detects it (a panel with no plate, or one whose item filled it corner to corner) and **re-keys the sheet as one plate automatically**. Read a zoned sheet you already have with `--zones auto` (default) or `--zones 3x2`. **`--refine`** re-cuts each located item from the UN-keyed sheet with the local ONNX matting model (free, ~1–2s each) for painterly/soft/furry/glassy art, falling back per item to the keyed cut when the matte comes back empty or fully opaque. **Plate color is still chosen for you** when generating flat (green magenta blue black white) and detected off an existing sheet. Per-item `holes`/`hole_pct`/`hollow` are still reported (console `⚠ N% hollow` at ≥20%, `--json`, `stickers.json`) under the smart keyer a flag usually means real art (ring/frame/donut), so it **warns, never blocks**; lift a stubborn item with `vidfarm mask --crop …`. `--output-format gif` emits 1-bit-alpha GIFs for GIF-only surfaces. IMAGE-only. Aliases: `stickers`, `sticker-sheet`. See recipe `cutout-graphics-for-explainers.md` → "A sticker pack". |
2162
2291
  | `vidfarm tts "…" [--style "…"] [--voice <v>] [--out <file>]` | (LOCAL-FIRST: your own OPENAI/GEMINI/OPENROUTER_API_KEY → audio file on disk; `--cloud` = `POST /api/v1/primitives/audio/speech` + poll, ElevenLabs on the platform key by default, `--own-key` for yours) | text → narration audio; `--cloud --voice <voice_id>` picks an ElevenLabs voice |
2163
2292
  | `vidfarm music "<prompt>" [--length <sec>] [--out <f>] [--own-key]` | `POST /api/v1/primitives/music/generate` (polls job) | prompt → music track (ElevenLabs; platform key + wallet by default, `--own-key` for yours) |
2164
- | `vidfarm voices [--own-key] [--limit N]` | `GET /api/v1/primitives/audio/voices` | list ElevenLabs voices (voice_id/name/labels) for `tts --voice`; default a voice + tell the user they can choose |
2293
+ | `vidfarm voices [--sample] [--search "…"] [--free\|--all] [--own-key] [--limit N]` | `GET /api/v1/primitives/audio/voices` | **Browse AND sample narration voices.** Default roster = the premium ElevenLabs catalog reached through **vidfarm's own ElevenLabs connection** — the user needs no ElevenLabs account, API key, or subscription; narration is billed as vidfarm wallet credits (pennies each). `--free` = the $0 local Kokoro roster (`--all` = both). `--sample` writes listenable clips to `./voice-samples` (`--sample-count`, `--sample-out`, `--sample-text`) and is **free on both tiers** — premium samples are ElevenLabs' own preview clips, free samples render locally — so it's safe in `minimize`. `--search` filters by name/labels/description. **In interactive mode play the samples and let the USER pick**; autonomous = default a voice and still say they can choose. `--own-key` lists the customer's own ElevenLabs account instead. |
2165
2294
  | `vidfarm stt <file\|url> [--out <base>] [--no-diarize]` (alias: `transcribe`) | (LOCAL-FIRST: local ffmpeg demux + your own key; `--cloud` = `POST /api/v1/primitives/audio/transcribe` + poll, ElevenLabs Scribe on the platform key by default, `--own-key` for yours) | video/audio → transcript in BOTH formats: simple subtitles (txt + SRT) and multi-speaker segments (json) |
2166
2295
  | `vidfarm place <dir> --src <url\|file> [--at\|--replace]` | (edits local composition.html; local files → serve disk store or temp upload) | drop media (URL **or local file**) into a gap / over a scene |
2167
2296
  | `vidfarm captions generate <dir> [--style <preset>] [--audio <f>\|--srt <f>\|--text "…"]` | (LOCAL-FIRST: STT on your own key — OpenAI = real word timestamps — then edits local composition.html) | transcribe narration → animated word-by-word caption cues |
@@ -2187,7 +2316,7 @@ The licensed harness also carries the **generative build workflow** guidance (ch
2187
2316
  | `vidfarm whoami` | `GET /api/v1/user/me` | who am I (also prints the active cost mode) |
2188
2317
  | `vidfarm cost-mode [minimize\|hybrid\|rich-ai\|pure-videogen]` | (local `~/.vidfarm/cost-mode.json`) | show/set the money-saving preference every billed command respects; no arg = show + explain each; `--clear` forgets it |
2189
2318
  | `vidfarm mode [interactive\|autonomous]` | local (`~/.vidfarm/interaction-mode.json`) | **The SECOND preference axis — ask it alongside cost mode.** Is the human in the loop? `interactive` = at checkpoints you hand them a copy-paste brief (an image prompt for a FREE web generator; a keyword list for sourcing clips) and they hand files back — usually BETTER output, and in `minimize` the only way to get custom art at all. `autonomous` = you finish solo within the budget. Independent of cost mode: every cost mode runs either way. No arg prints the pair + the explainer to relay; `--clear` forgets; per-run `--interactive`/`--autonomous`. Aliases: `interaction-mode`, `interaction`. |
2190
- | `vidfarm handoff image --theme "<what>" [--items "a,b,c"] [--style …] [--single]` | local (pure text) | **The interactive-mode IMAGE brief.** Prints the exact prompt, the numbered steps, the free tools (meta.ai / ChatGPT / Gemini / HF Spaces) and the follow-up command. Defaults to a **sticker pack**: ONE sheet holding every item on a chroma plate → `vidfarm sticker-pack` splits it for $0. Picks a plate the art won't collide with (green art → magenta plate), spells out the **key-safe fill rules** (closed solidly-filled shapes, no outline-only/hollow art, nothing in a near-plate shade, fully opaque) so the sheet doesn't come back as rims around holes, and carries that `--key-color` into the follow-up. `--single` for one subject. |
2319
+ | `vidfarm handoff image --theme "<what>" [--items "a,b,c"] [--style …] [--single]` | local (pure text) | **The interactive-mode IMAGE brief.** Prints the exact prompt, the numbered steps, the free tools (meta.ai / ChatGPT / Gemini / HF Spaces) and the follow-up command. Defaults to a **sticker pack**: ONE sheet holding every item on a chroma plate → `vidfarm sticker-pack` splits it for $0. Picks a plate the art won't collide with (green art → magenta plate), spells out what the local keyer actually needs (a crisp silhouette in a different color from the plate, sealed shapes, clear gaps between items hollow art and plate-colored detail INSIDE a shape are fine now), and carries that `--key-color` into the follow-up. `--single` for one subject. `--zoned` asks instead for a color-block sheet (one panel colour per item) and hands back a `--zones RxC` follow-up — worth it when the pack's own colors fight one plate, but leave it off for a free consumer tool that may not follow a grid. |
2191
2320
  | `vidfarm handoff raws --keywords "a,b" [--platforms tiktok,youtube] [--count N] [--purpose "…"]` | local (pure text) | **The interactive-mode CLIP-SOURCING brief** — the bottom rung of the sourcing ladder (browser control → `clipper`/`raws scan --cloud` → public raws → the human). Prints what to search, how to download (a Google *search* for a downloader, never a link that rots), and the import command for when the folder is ready. |
2192
2321
  | `vidfarm storyboard [dir] [--init] [--frames "Title\|scene,…"] [--json]` | local (`STORYBOARD.md` / `SCRIPT.md`) | **The plan pass, and a core part of the composition format.** Scaffolds/reads the project's `STORYBOARD.md`: ordered frames with `duration`/`status`/`src`/`scene`/`voiceover`. The Vidfarm editor renders this file in its **Storyboard** view (contact sheet + per-frame comments + `outline → built → animated` progress), so it's the cheapest place to get a director's approval before building. Not to be confused with `vidfarm sequence` (which GENERATES storyboard images for the pure-videogen pipeline). Alias: `plan`. |
2193
2322
  | `vidfarm wallet [--job <id>\|--tracer <t>] [--limit <n>]` | `GET /api/v1/user/me/wallet` | cost log: balance + lifetime spend + recent charges. `--job <renderJobId>` prints **what that one video cost** (sums its charges); `--tracer <t>` sums a tracer. Cloud-only; readable on the free plan too (shows $0.00). Aliases: `spend`, `costs` |
@@ -2211,10 +2340,11 @@ The licensed harness also carries the **generative build workflow** guidance (ch
2211
2340
  | `vidfarm raws preset list\|run\|save` / `raws export <ids…> --to <dir>` | (local library) | saved queries; copy raw MP4s out |
2212
2341
  | `vidfarm lint <dir\|composition.html>` | (local static validation) | pre-publish composition check: timing, overlaps, preset names, media src |
2213
2342
  | `vidfarm stills <dir> [--at 0,2.5,…] [--sheet]` | (local in-process render of PNG frames) | visually verify an edit without a full render. **`--sheet` also tiles them into one contact sheet** (`<out>/contact-sheet.png`, `--sheet-out`/`--sheet-width` to tune) — the whole-video review pass: read it as ONE image and sequence-level drift (uneven margins, three type sizes, a wandering accent colour, N identical beats, a jarring join) becomes obvious where per-scene checks never see it |
2214
- | `vidfarm qa <dir\|composition.html> [--regime <name\|path>…] [--json] [--strict]` | (local static QA — **devcli-only**, no cloud/REST twin) | **social-native QA: HTML slop + first frame + font regime. Run it on EVERY video you produce.** `--regime` grades against a QA_REGIME.md too (stackable). Free, instant, feedback-only |
2215
- | `vidfarm regime list\|show <name\|path>\|init <name> [--out <path>]` | (local — **devcli-only**) | QA_REGIME.md: the director's own per-style quality contract. `init` copies a bundled base to edit the pattern for bulk/scripting mode |
2343
+ | `vidfarm qa <dir\|composition.html> [--harness <name\|path>…] [--json] [--strict]` | (local static QA — **devcli-only**, no cloud/REST twin) | **social-native QA: HTML slop + first frame + font regime. Run it on EVERY video you produce.** `--harness` grades against a HARNESS.md too (stackable). Free, instant, feedback-only |
2344
+ | `vidfarm harness list\|show <ref> [--dna <strand>]\|init <name> [--out <path>]\|derive <forkId\|dir>\|check <dir>` | (local — **devcli-only**) | **HARNESS.md: the reusable AI harness for one format or template.** `init` copies a bundled base to edit; `derive` turns a decomposed template's DNA into one ("give me the harness for this template_id"); `check` is `vidfarm qa` under the harness noun |
2216
2345
  | `vidfarm doctor` | (local environment triage) | check ffmpeg/node/keys/agent CLI/poisoned env + list local serve/preview processes before debugging anything else; `--kill-orphans` reaps dead servers squatting ports (fixes the "Waiting for preview server…" hang) |
2217
2346
  | `vidfarm skills list\|add <name>\|update` | `GET /skill-pack/index.json` · `/skill-pack/:name/*` | install/refresh skill packs (see "Skill packs — import on demand") |
2347
+ | `vidfarm skill ls\|show <path>\|search "<term>"\|path` | (local — **offline, no account**) | **Read this pack straight off disk.** A full copy ships inside the devcli tarball and is pinned to the installed version. `search` greps all 22 files at once — the cheapest way to find one paragraph without loading a whole reference |
2218
2348
  | `vidfarm tts "…" --engine local` / `vidfarm stt <file> --engine whisper` | (keyless LOCAL engines: Kokoro-82M TTS, whisper.cpp STT) | narration + word-timestamp transcripts with zero keys and zero accounts |
2219
2349
  | `vidfarm remove-background <video\|image>` | (local ONNX matting — free) | transparent-subject media for occlusion captions/cutouts (arbitrary/messy background; for a FLAT solid background use `remove-background-greenscreen`) |
2220
2350
  | `vidfarm capture <url>` | (local headless-Chrome capture) | website screenshots/assets for website-to-video flows |
@@ -2232,8 +2362,8 @@ The licensed harness also carries the **generative build workflow** guidance (ch
2232
2362
  vidfarm qa ./work # human-readable findings + verdict
2233
2363
  vidfarm qa ./work --json # machine-readable: rule / severity / where / fix
2234
2364
  vidfarm qa ./work --strict # ALSO exit 1 on slop (only if you want a CI gate)
2235
- vidfarm qa ./work --regime hooks # + grade against a QA_REGIME.md (repeatable; also
2236
- # auto-discovers ./work/QA_REGIME.md)
2365
+ vidfarm qa ./work --harness hooks # + grade against a HARNESS.md (repeatable; also
2366
+ # auto-discovers ./work/HARNESS.md)
2237
2367
  ```
2238
2368
 
2239
2369
  **Run this on every video you produce.** It is free, instant (pure DOM, no ffmpeg/Chrome/network), and it is the only automated check for the thing that most often ruins an agent-made video: **HTML slop**. Compositions are authored in HTML, so an agent's web-page instincts leak straight onto the frame as landing-page furniture that appears on every website and in **zero** real TikToks.
@@ -2259,15 +2389,22 @@ What it flags:
2259
2389
  | `font-regime` | error/warn | A text layer in a website body font (Inter/Roboto/Arial/system-ui → **error**) or any family outside the imported regime (Montserrat, TikTok Sans, Abel, Source Code Pro, Yesteryear → warn, it silently falls back at render) |
2260
2390
  | `font-size` / `font-weight` | error/warn | `font-size:0` (invisible) is an error; sub-2.6%-of-canvas-width text and weight <600 warn |
2261
2391
  | `caption-safe-zone` | warn | Text outside the 8%–85% band on a **portrait** canvas (landscape/square are exempt) |
2392
+ | `caption-oversize` | warn | Display-size type (>7.5% of canvas width) on a line of **5+ words** — it runs edge-to-edge, wraps, covers the frame, and forces a full-width plate. Both signals required, so a giant 2-word hook card passes |
2393
+ | `wall-of-text` | warn | One **static** text layer carrying 14+ words — a paragraph, not a caption. Page it into 3–5-word kinetic cues (`captions generate --style word-pop`). Layers already part of an animated caption run are exempt |
2394
+ | `dead-air` | warn | A gap of **≥2.5s between cues** with nothing on screen to read (needs 3+ cues, so a two-card title sequence is exempt). Dead screen time is a free exit — cut it and `ripple` the hole closed |
2395
+ | `dead-tail` | warn | The video keeps running **>1.5s after the last word** — an outro, an end card, or an untrimmed clip. End on the bait |
2396
+ | `slow-scene` | warn | One clip >6s **and** >2.5× the median clip length — judged against the video's OWN rhythm, so a deliberately slow piece or a single-take talking head passes |
2262
2397
  | `thumbnail-blank-open` | error | Nothing on screen at **t=0** — the opening clip starts late, so the poster frame is black |
2263
2398
  | `thumbnail-fade-in` | error/warn | An **entrance** transition on the FIRST clip: `fade-black`/`fade-white`/`flash`/`smoke` → **error** (frame 0 is a flat solid); any other preset → warn (frame 0 caught mid-move). Junction transitions on later clips are never flagged |
2264
2399
  | `thumbnail-no-hook-text` | warn | The composition has text, but none of it is up at t=0 — the poster carries no hook words. Ignorable when you're deliberately opening on a clean face/product shot |
2265
2400
 
2266
2401
  Every finding carries a concrete `fix` line — the answer is always "say it as timed text on the footage", never just "delete it". Fold `--json` into scripted batch runs to QA N variants at once.
2267
2402
 
2268
- **`vidfarm qa` is a static DOM checkit cannot see the rendered video.** It never looks at pixels, motion, spacing, colour drift, pacing, or the joins between scenes, so a clean `qa` run says nothing about whether the video reads as one coherent piece. That judgment is a separate, mandatory pass: tile stills into a contact sheet, read it as an image, and check balance/spacing/type/colour/rhythm across the whole sequence. It also can't catch a **frozen render** (every frame identical while duration, frame count and audio hash all pass), which is why you compare frames from two different scenes. Full method: `references/reviewing-renders.md`.
2403
+ **Every run ends by telling you to go watch the video — that instruction is part of the output, not a footnote.** `vidfarm qa` closes with a `▶ NOW WATCH THE VIDEO this check never did` block (and a `watch_the_video: { required: true, why, steps[] }` object in `--json`), printed on **clean** runs too, because a green tick on DOM attributes is the single easiest thing to mistake for a reviewed video. The steps are dir-aware and paste-ready: render, `stills --sheet` *open the contact sheet*, read it as one sequence, judge each caption against its picture, compare two different scenes (a frozen render passes every mechanical check), measure the audio with `volumedetect`, and report what you measured separately from what you judged. **Do them.** Reporting "QA passed" to a director without opening a frame is not a review, and the tool now says so to your face.
2404
+
2405
+ **`vidfarm qa` is a static DOM check — it cannot see the rendered video.** In particular it can tell you a caption is *too big* or *outside the safe zone*, but never whether it sits in the **empty** part of the frame — that needs pixels, so it stays your job: `vidfarm stills <dir> --at <t>`, look, then place (see `references/editor-workflows.md` → "TikTok-native caption standard"). It never looks at pixels, motion, spacing, colour drift, pacing, or the joins between scenes, so a clean `qa` run says nothing about whether the video reads as one coherent piece. That judgment is a separate, mandatory pass: tile stills into a contact sheet, read it as an image, and check balance/spacing/type/colour/rhythm across the whole sequence. It also can't catch a **frozen render** (every frame identical while duration, frame count and audio hash all pass), which is why you compare frames from two different scenes. Full method: `references/reviewing-renders.md`.
2269
2406
 
2270
- **The two halves, and why the tool only claims one.** Everything above is universal and mechanical. The half that decides whether a *particular* video is any good — is the hook legible cold, does the loop close, is this variant genuinely different from its siblings — is the director's, and it lives in a **`QA_REGIME.md`** (see "Scripting mode" above). Pass one with `--regime <name|path>` (repeatable, and a `QA_REGIME.md` sitting next to the composition is picked up automatically): its `checks:` front matter is settled deterministically alongside the built-ins, and its `- [ ]` checklist comes back as **review items you must answer yourself**. `vidfarm qa` deliberately never fakes a verdict on those — a "PASS" it couldn't have earned is worse than no check at all.
2407
+ **The two halves, and why the tool only claims one.** Everything above is universal and mechanical. The half that decides whether a *particular* video is any good — is the hook legible cold, does the loop close, is this variant genuinely different from its siblings — is the director's, and it lives in a **`HARNESS.md`** (see "Scripting mode" above). Pass one with `--harness <name|path>` (repeatable, and a `HARNESS.md` sitting next to the composition is picked up automatically): its `checks:` front matter is settled deterministically alongside the built-ins, and its `- [ ]` checklist comes back as **review items you must answer yourself**. `vidfarm qa` deliberately never fakes a verdict on those — a "PASS" it couldn't have earned is worse than no check at all.
2271
2408
 
2272
2409
  ## Cost mode — the devcli's money-saving guardrail
2273
2410
 
@@ -2376,6 +2513,25 @@ The customer-facing walkthrough (the "VidFarm Walkthrough Tutorial" course) is p
2376
2513
 
2377
2514
  Both are public and read-only (no auth). Prefer these to guessing steps — quote the real chapter and link the reader to its `url`. Chapters cover onboarding/setup, the operating funnel (angles/hooks/awareness), each guided edit demo (recaption, product tease, remix-with-raws, actor replacement, animate-static-book, drama series, product promo, motion explainers), sourcing/clipping raws, the wallet, cancellation/refunds, and the developer devcli/scripting/free-mode chapters.
2378
2515
 
2516
+ ## The director pack ships inside the devcli — read it offline
2517
+
2518
+ Installing `@officexapp/vidfarm-devcli` puts a **complete copy of this pack on disk**, pinned to that CLI version. You never have to be online, logged in, or in a project with `.agents/skills/` to read it:
2519
+
2520
+ ```bash
2521
+ vidfarm skill ls # every file, with sizes
2522
+ vidfarm skill show primitives # shorthand resolves to references/primitives.md
2523
+ vidfarm skill show harnesses/README.md # or an exact path
2524
+ vidfarm skill search "greenscreen" # grep all of it — find the paragraph, then open that file
2525
+ vidfarm skill path # where the bundled copy lives
2526
+ ```
2527
+
2528
+ **Prefer `skill search` over opening a big reference.** `editor-workflows.md` is ~650 lines and `automation-and-local-dev.md` ~520; a grep that returns `references/primitives.md:214` costs almost nothing and tells you exactly which file to load.
2529
+
2530
+ Two things this does NOT mean:
2531
+
2532
+ - **Pinned, not live.** The bundled copy matches the installed CLI — which is the pairing that actually works, since a newer skill against an older binary is the usual cause of *"the skill says to do X but the command 404s"*. For the host's latest, `vidfarm skills add vidfarm` (installs into a project) or `vidfarm skill --print --remote`. When they disagree, update **both halves together**: <https://vidfarm.cc/update.md>.
2533
+ - **Documentation, not entitlement.** Reading about a paid primitive offline does not make it run offline. The **free-local** half genuinely needs nothing — clip hunting, hyperframes, `vidfarm serve` render, `vidfarm qa`, harnesses, `vidfarm dedupe`, Kokoro TTS, whisper STT. The **paid-cloud** half still needs `vidfarm login` and a network call: AI image/video/voice generation, hosted render, `recycle`, `download-video`, marketplace, and the hosted file directory. Tell the director which half a plan lands in *before* you build it.
2534
+
2379
2535
  ## Skill packs — import on demand (HyperFrames-grade authoring power)
2380
2536
 
2381
2537
  This skill stays lean on purpose. Deep authoring craft lives in **skill packs** — Vidfarm's whitelabel of the open-source `hyperframes` skill suite (same engine as `vidfarm hf` / `vidfarm render`, Vidfarm-branded) plus Vidfarm's own media pack — vendored on the Vidfarm host and installed only when a task needs them. Never install skills from upstream vendor orgs or third-party registries; the vidfarm mirror is the source (`vidfarm skills add <name>` fetches `GET /skill-pack/:name/*` with hash verification into `.agents/skills/` + a `.claude/skills/` link, pinned in `skills-lock.json`; `vidfarm skills list` shows what is available/installed; `vidfarm skills update` refreshes pins).
@@ -2969,7 +3125,7 @@ Use this when a coding agent is doing the work locally or the user wants a repro
2969
3125
  3. Read `./work/.harness/agent-guide.md` and `./work/.harness/context.json` before editing.
2970
3126
  4. Make deterministic edits to `composition.html` and optionally `composition.json`.
2971
3127
  5. Validate with `vidfarm lint` or `vidfarm stills` when useful. **Always look at `vidfarm stills ./work --at 0`** — that frame becomes the thumbnail, so it must not be black, empty, or mid-fade.
2972
- 6. **QA before you render: `vidfarm qa ./work`.** Free, instant, devcli-only. It blocklists HTML slop (CTA buttons, benefit chip rows, a lone pill around a static stat/label, frosted cards, gradient text, web-page classes/fonts), checks the caption font regime + safe zone, and flags a blank/fading first frame (the thumbnail). Feedback only — exit 0 even on findings, never automatic — but it catches the #1 tell of an agent-made video, so run it on every production. Fix what's real, ignore what's a deliberate style call, then render.
3128
+ 6. **QA before you render: `vidfarm qa ./work`.** Free, instant, devcli-only. It blocklists HTML slop (CTA buttons, benefit chip rows, a lone pill around a static stat/label, frosted cards, gradient text, web-page classes/fonts), checks the caption font regime + safe zone, flags oversized captions and static walls of text, and flags a blank/fading first frame (the thumbnail). It cannot see pixels, so *where in the frame* the caption sits is still on you — which is why every run ends with a **`▶ NOW WATCH THE VIDEO`** block: render, `vidfarm stills ./work --sheet`, open the contact sheet, and judge each caption against its actual picture. Do that before you report the video as done. Feedback only — exit 0 even on findings, never automatic — but it catches the #1 tell of an agent-made video, so run it on every production. Fix what's real, ignore what's a deliberate style call, then render.
2973
3129
  7. Render with `vidfarm render <forkId> --dir ./work --wait`.
2974
3130
  7b. **Review the render as a whole before you approve — this is the step that most changes quality.** `vidfarm qa` and `lint` are static checks on the DOM; neither can see the video. Tile ~12 stills into one contact sheet and read it as an image — `vidfarm stills ./work --sheet` does both in one command (add `--at 0,2,4,…` to pick the timestamps): consistent margins, one type scale, one accent colour, deliberate pacing, no jarring join, no dead band under top-anchored content, end card settled ≥2s before the last frame. Compare frames from two different scenes — a frozen render (overlay pass without `-loop 1`, assets outside the composition root) passes duration, frame-count and audio-hash checks while every frame is identical. Check the mix by measurement, not by ear. Full method + the six most common defects: `references/reviewing-renders.md`.
2975
3131
  8. **Ask about deduplication before you approve** — "is this going out more than once (several accounts, another platform, a re-post later)?" If yes, run `vidfarm dedupe ./final.mp4 [--variants N]` on the **exported** MP4 (free, local ffmpeg, no re-render) and approve each variant separately. Asking here rather than after publication is what avoids paying for a second render. See `references/core-workflows.md` → *Deduplicate before you publish*.
@@ -2979,15 +3135,15 @@ Use this when a coding agent is doing the work locally or the user wants a repro
2979
3135
 
2980
3136
  Prefer this path for batch work, CI-like edits, or when the user wants free local rendering through `vidfarm serve`.
2981
3137
 
2982
- ## Recipe: Bulk Video Generation (Scripting Mode) with a QA_REGIME.md
3138
+ ## Recipe: Bulk Video Generation (Scripting Mode) with a HARNESS.md
2983
3139
 
2984
3140
  Use this when the director wants **volume** — daily posting, hook tests, one video per clip in a pool, N variants of a template. Ask first if you're not sure: *"One video, or should we set this up as a repeatable batch?"* If they want volume, this is the shape.
2985
3141
 
2986
- The thing that makes bulk work is not the loop — loops are easy. It's that **nobody is going to watch variant #37 as carefully as variant #1**, so the standard has to be written down before the loop runs. That's the `QA_REGIME.md`.
3142
+ The thing that makes bulk work is not the loop — loops are easy. It's that **nobody is going to watch variant #37 as carefully as variant #1**, so the standard has to be written down before the loop runs. That's the `HARNESS.md`.
2987
3143
 
2988
3144
  ### 0. Read the craft harness first
2989
3145
 
2990
- `references/hooks-and-virality.md` — the four charges (hook / loop / payoff / bait), the three gates, and the anti-patterns that only bite at volume. Two of them decide whether this batch is worth running at all: **a different noun is not a different hook** (twenty variants of one sentence with the nouns swapped is one video), and **never point a generator at your grader** (a model writing hooks scored by the same model converges on the rubric, not on what works — scores climb, nothing improves). The regime catches defects; it does not rank winners.
3146
+ `references/hooks-and-virality.md` — the four charges (hook / loop / payoff / bait), the three gates, and the anti-patterns that only bite at volume. Two of them decide whether this batch is worth running at all: **a different noun is not a different hook** (twenty variants of one sentence with the nouns swapped is one video), and **never point a generator at your grader** (a model writing hooks scored by the same model converges on the rubric, not on what works — scores climb, nothing improves). The harness catches defects; it does not rank winners.
2991
3147
 
2992
3148
  ### 1. Agree the variant axis — before any code
2993
3149
 
@@ -3001,14 +3157,22 @@ vidfarm pull <forkId> --dir ./work # one canonical base fork per batch
3001
3157
 
3002
3158
  Read `./work/.harness/agent-guide.md` first, as always.
3003
3159
 
3004
- ### 3. Install and EDIT the regime
3160
+ ### 3. Install and EDIT the harness
3161
+
3162
+ Two ways in, depending on where the format came from:
3005
3163
 
3006
3164
  ```bash
3007
- vidfarm regime list # short-form | hooks | ugc-testimonial | explainer | product-demo
3008
- vidfarm regime init hooks --out ./work/QA_REGIME.md
3165
+ # (a) From a bundled base when the format is one you're defining
3166
+ vidfarm harness list # short-form | hooks | ugc-testimonial | explainer | product-demo
3167
+ vidfarm harness init hooks --out ./work/HARNESS.md
3168
+
3169
+ # (b) From the template you're batching — when the format is one you're REPLICATING
3170
+ vidfarm harness derive <forkId> --out ./work/HARNESS.md # the decomposition, as a harness
3009
3171
  ```
3010
3172
 
3011
- Then **edit it with the director**. The bundled file is a starting point; the parts that matter are the ones they add who the viewer is, their banned vocabulary, the compliance line, the pacing this account actually uses. A regime nobody edited isn't about their videos. Existing regime somewhere else on disk? Just point at it: `--regime ./brand/HOUSE_RULES.md`. They stack.
3173
+ (b) is what a director means by *"give me the harness for this template_id"*: the decompose pass already extracted the template's viral / visual / structural / audio / build DNA, and `derive` folds those strands into the same editable Markdown a bundled base produces. If the fork was never decomposed, run `vidfarm decompose` first.
3174
+
3175
+ Either way, **edit it with the director**. The generated file is a starting point; the parts that matter are the ones they add — who the viewer is, their banned vocabulary, the compliance line, the pacing this account actually uses. A harness nobody edited isn't about their videos. A *derived* harness has the extra failure mode of sounding authoritative: it was written by a model that watched one video, so its "unknown" lines and its confident-but-wrong lines both need a human pass. Existing harness somewhere else on disk? Just point at it: `--harness ./brand/HOUSE_RULES.md`. They stack.
3012
3176
 
3013
3177
  ### 4. Source the N cheaply
3014
3178
 
@@ -3019,13 +3183,13 @@ vidfarm public-raws --category scroll-stoppers --limit 20 --json > pool.json
3019
3183
 
3020
3184
  A curated shelf is a pre-tagged, free, already-hosted clip pool — the cheapest way to get N distinct variants without N downloads or N generation calls.
3021
3185
 
3022
- ### 5. Loop: edit → QA against the regime → render
3186
+ ### 5. Loop: edit → QA against the harness → render
3023
3187
 
3024
3188
  ```bash
3025
3189
  for VARIANT in "${VARIANTS[@]}"; do
3026
3190
  SLUG="$(echo "$VARIANT" | tr ' ' '-' | cut -c1-40)"
3027
3191
  vidfarm set-text ./work --layer hook --text "$VARIANT"
3028
- vidfarm qa ./work --json > "qa/$SLUG.json" # ./work/QA_REGIME.md auto-discovered
3192
+ vidfarm qa ./work --json > "qa/$SLUG.json" # ./work/HARNESS.md auto-discovered
3029
3193
  jq -e '.ok' "qa/$SLUG.json" >/dev/null || { echo "skipped $SLUG"; continue; }
3030
3194
  vidfarm render "$FORK_ID" --dir ./work --out "renders/$SLUG.mp4" --tracer "batch-$SLUG"
3031
3195
  done
@@ -3062,11 +3226,11 @@ Read the sheets. In a batch you're looking for two different things: **per-video
3062
3226
 
3063
3227
  ### 6. Answer the review items — don't skip this
3064
3228
 
3065
- The regime's `- [ ]` checklist comes back on every run because the CLI *can't* settle it. Machine checks catch a 13-word hook or a black first frame; only you can answer "is this variant genuinely different from its siblings?" or "can the viewer guess the withheld answer?" **Report both halves honestly**: what the machine checked, and what you judged. A batch report claiming a clean pass on the judgment half is worse than no report.
3229
+ The harness's `- [ ]` checklist comes back on every run because the CLI *can't* settle it. Machine checks catch a 13-word hook or a black first frame; only you can answer "is this variant genuinely different from its siblings?" or "can the viewer guess the withheld answer?" **Report both halves honestly**: what the machine checked, and what you judged. A batch report claiming a clean pass on the judgment half is worse than no report.
3066
3230
 
3067
- ### 7. Feed what you learn back into the regime
3231
+ ### 7. Feed what you learn back into the harness
3068
3232
 
3069
- When the director says "the label-framed hooks all died" or "anything over 30s tanked", write it into `QA_REGIME.md` as a rule or a checklist line — with the reason attached, so the next agent doesn't argue it away. The compositions are disposable; **the regime is the artifact that compounds across batches.**
3233
+ When the director says "the label-framed hooks all died" or "anything over 30s tanked", write it into `HARNESS.md` as a rule or a checklist line — with the reason attached, so the next agent doesn't argue it away. The compositions are disposable; **the harness is the artifact that compounds across batches.**
3070
3234
 
3071
3235
  ### Cost note
3072
3236
 
@@ -3090,7 +3254,7 @@ The mechanical trio — **generate on a chroma plate → key it out → trim to
3090
3254
  ```
3091
3255
  One accent color for the active word, everything else near-black. No outline/stroke, no drop shadow, no pill — those exist to survive busy footage and just add noise on white.
3092
3256
 
3093
- **Those hexes are the answer for a white stage, not the answer.** They are one instance of a general rule: **caption color, active-word color and plate are chosen by MEASURING the background behind the caption band, never by taste or habit.** On a near-black stage the same flags ship a bright plate the design never asked for and an active word nobody can read. The measurement procedure and the three treatments live in `regimes/short-form.QA_REGIME.md` → "Caption styling is measured off the background" — read it before you copy the line above onto anything that isn't white.
3257
+ **Those hexes are the answer for a white stage, not the answer.** They are one instance of a general rule: **caption color, active-word color and plate are chosen by MEASURING the background behind the caption band, never by taste or habit.** On a near-black stage the same flags ship a bright plate the design never asked for and an active word nobody can read. The measurement procedure and the three treatments live in `harnesses/short-form.HARNESS.md` → "Caption styling is measured off the background" — read it before you copy the line above onto anything that isn't white.
3094
3258
  - **Female TTS narration.** Default to a warm, friendly **female** voice and say which one you picked: local-first `vidfarm tts "<script>" --voice coral` (OpenAI — `nova` if the script wants more energy, `sage` for calmer), `--voice Kore` or `Leda` on Gemini, or `vidfarm voices` → `vidfarm tts --cloud --voice <voice_id>` on ElevenLabs. Tell the director they can swap it in one flag.
3095
3259
  - **Clean and simple wins.** One idea on screen at a time. Two or three cutouts per beat, not eight. Generous white space, one accent color, one font. When in doubt, remove an element rather than add one.
3096
3260
 
@@ -3130,28 +3294,58 @@ Out comes `./stickers/sticker-01-red-barn.png`, `sticker-02-tractor.png`, … ea
3130
3294
  - `--gap <pct>` (default 1.2) — how far apart two islands must be to count as separate items. **Two items came out as one sticker → lower it** (or ask for wider spacing on the sheet). **One item came out split in two → raise it** (its parts, e.g. a floating antenna or a dotted arrow, weren't bridged).
3131
3295
  - `--min-area <pct>` (default 0.15) — drops key speckle. There is **no maximum** — see the size note below.
3132
3296
  - `--preset`/`--key-color` — match the plate (default `#00FF00`). `--pad`, `--alpha-threshold`, `--output-format png|webp|gif`, `--prefix`, `--max-items`, `--keep-plate`/`--keep-sheet` behave like `cutout`'s.
3297
+ - `--sheet-mode auto|zoned|flat`, `--zones auto|off|RxC`, `--zone-cols <n>`, `--key-mode smart|flat`, `--refine` — how the plate(s) are laid out and removed. All four are explained below ("How the key actually works now").
3133
3298
  - Stubborn item? Fall back to one hand-measured `vidfarm mask ./sheet.png --crop x,y,w,h --flat "#00FF00"` for that one; the rest of the pack still comes from `sticker-pack`.
3134
3299
 
3135
- **Pick a plate the ART doesn't use this is the #1 way a pack comes out broken.** A chroma key removes exactly ONE color. A sticker pack of leaves, frogs, plants, money, or "eco" iconography on a GREEN plate keys holes straight through the artwork, and it looks like the cut failed when really the plate was wrong. The commands handle it two ways:
3300
+ **How the key actually works nowCONNECTIVITY, not color matching.** This is the thing to internalize, because it removes the constraint that used to force sticker art to stay flat and simple. `sticker-pack` and `cutout` do **not** delete every pixel that looks like the plate. They score each pixel's distance to its plate color, then **flood-fill inward from the edge of the sheet** through plate-ish pixels, and delete only what the fill **reaches**. Consequences, all of them useful:
3136
3301
 
3137
- - **Generating?** `sticker-pack --generate` / `cutout --generate` read the subject text and move the plate off any hue it mentions — green (`#00FF00`) magenta (`#FF00FF`) blue (`#0047BB`) black white then print which plate they chose and why. Say it back to the director when it moves: *"Your items are mostly green, so I generated them on a magenta plate."*
3138
- - **Splitting a sheet you already have?** The plate is **detected from the sheet's four corners** (a sticker sheet always has plate in the corners), so a red/purple/blue sheet handed back from a free web generator just works with no flags.
3302
+ - **Plate-colored art survives if it's inside the item.** A green leaf on a green sheet, a `#00FF00` eye, a highlight in the plate hue unreachable from the sheet edge, therefore not background, therefore kept. The console tells you when it mattered: *"kept 10,000 plate-colored pixels INSIDE the art that a flat key would have punched out."*
3303
+ - **Outline / line-art shapes keep their middles.** The old "rim around a see-through hole" failure is a property of the flat chromakey, not of keying. An enclosed interior can't be reached by the fill.
3304
+ - **Edges come out clean.** Boundary pixels get a real coverage estimate and the plate is **un-mixed out of each one individually** (`art = (C − (1−α)·plate)/α`, stored as straight alpha), so there's no green fringe. That's strictly better than ffmpeg's global `despill`, which rebalances every pixel and discolors plate-hued art in the interior.
3305
+ - **`--key-mode flat` brings the old behaviour back.** It's the cloud primitive's exact filter chain — use it to reproduce a cloud render bit-for-bit, or as a simple fallback.
3139
3306
 
3140
- Override whenever you know better: `--key-color "#FF00FF"` or `--preset magenta` pins the plate (and turns the auto-pick off); `--no-auto-key` forces plain green. If a pack's palette collides with *every* standard plate a rainbow icon set — split it across two sheets on two different plates rather than fighting `--tolerance`. And when a sticker is a *green* subject, never let the user generate it on green: the hand-off brief (`vidfarm handoff image`) already picks the plate for them and carries the matching `--key-color` into the follow-up command.
3307
+ What the key still can't do for you: an item whose **outer edge** is the plate color dissolves into it, and anything that **fades** into the plate (soft glow, blur, drop shadow) has no crisp silhouette to find.
3141
3308
 
3142
- **Then make the ART key-safe too CLOSED, SOLIDLY FILLED shapes.** Picking the right plate is only half of surviving the key. The other half is the failure you actually see in the wild: you ask for "sticker icons on a green plate" and the model draws **outline art** — a colored stroke with the shape's *interior left as bare plate*. On the sheet it looks perfect. After the key, the interior is gone with the background, and every sticker composites as a **rim floating around a see-through hole** (an apple that's just an apple-shaped outline, a clock with no face). Same result from three cousins: a fill that's a *near-shade* of the plate (the keyer works on tolerance, not exact match), a **translucent/glassy/glowing** material that lets the plate through, and a **soft glow or drop shadow** that fades into the plate at the edges.
3309
+ **ONE PLATE COLOR PER STICKER`--sheet-mode zoned`.** The silhouette constraint is what "one plate per sheet" makes painful: the more items on the sheet, the more of the palette is off-limits to all of them. So stop giving the sheet one background. A **zoned** sheet is a grid of solid color **panels**, one item per panel, each panel's plate chosen against **that item**:
3310
+
3311
+ ```
3312
+ # 6 items → a 3×2 grid of color panels, each item's plate picked against its own art
3313
+ vidfarm sticker-pack --generate "pond life, flat vector" \
3314
+ --items "green frog,pink lotus,blue heron,white pebble,yellow reed,orange koi" \
3315
+ --sheet-mode zoned --out-dir ./stickers
3316
+
3317
+ # A zoned sheet from a web tool: recover the panels from the sheet's own edges
3318
+ vidfarm sticker-pack ./sheet.png --zones auto # (this is the default)
3319
+ vidfarm sticker-pack ./sheet.png --zones 3x2 # or declare the grid you asked for
3320
+ ```
3321
+
3322
+ - The green frog sits on magenta while the pink lotus sits on green — **in the same image job**. Item art may use **any** palette, including the colors of the *other* panels.
3323
+ - Each panel is keyed independently, with its color read back off **that panel's own corners** (image models drift the exact hue they were told, so the sheet is the source of truth, not the prompt).
3324
+ - **Names stop being guessed.** On a flat sheet, `--items` is matched to items in reading order, which goes off-by-one the moment two items merge. On a zoned sheet, panel *N* holds the item you asked for in panel *N* — so the mapping is exact, and `stickers.json` records each sticker's `panel` and `plate`.
3325
+ - `--sheet-mode auto` (the default) zones a generation of 2+ named items and stays flat otherwise. `--zone-cols <n>` forces the grid width.
3326
+
3327
+ **The FLAT one-color sheet is still first-class — reach for it with a weaker image model.** A cheap or small model will paint one background no matter how the grid is described. That's fine and fully supported:
3328
+
3329
+ - `--sheet-mode flat` asks for the classic single-plate sheet (and `--key-mode smart` still applies, so hollow art and plate-colored interiors are still safe).
3330
+ - If you asked for zones and the model ignored them, **keying detects it and re-keys the sheet as one plate automatically** — either because a "panel" had no plate to remove, or because its item filled the panel corner to corner (so the item's own color read as the plate). It says so on the console and in `stickers.json` (`key_note`). Zoning can't strand you.
3331
+
3332
+ **Plate color is still chosen for you on a flat sheet.** `--generate` reads the subject and moves the plate off any hue it mentions — green (`#00FF00`) → magenta (`#FF00FF`) → blue (`#0047BB`) → black → white — and prints which it chose and why; say it back to the director when it moves (*"your items are mostly green, so I generated them on a magenta plate"*). Splitting a sheet you already have, the plate is read off the sheet itself, so a red/purple/blue sheet from a free web generator just works. Pin it with `--key-color "#FF00FF"` / `--preset magenta`, or `--no-auto-key` for plain green.
3333
+
3334
+ **Soft, painterly, furry or glassy art → `--refine`.** Every chroma key needs a crisp silhouette, so watercolor edges, fur, glow, glass and cast shadows are out of scope for the keyer no matter how clever the fill is. `--refine` handles them: the keyer's job shrinks to *locating* each item, and the item is then re-cut from the **un-keyed** sheet with the local ONNX matting model (free, ~1–2s each), which ignores color entirely and produces a genuine soft matte.
3335
+
3336
+ ```
3337
+ vidfarm sticker-pack ./painterly-sheet.png --refine --out-dir ./stickers
3338
+ ```
3143
3339
 
3144
- None of that is fixable after generation you cannot key back pixels that were never drawn. So it belongs in the **prompt**, every time:
3340
+ It's checked, not trusted: the matte is measured, and if it came back **empty** or **fully opaque** (the model didn't see a subject) that item keeps its keyed cut and the console says so. Flat vector art is exactly where matting fails and the chroma cut is better — so **don't use `--refine` by default**, only when the art is genuinely soft.
3145
3341
 
3146
- > Every object must be a CLOSED, SOLIDLY FILLED shape outlines and strokes must enclose an opaque fill of a different color. No outline-only, hollow, or line-art objects; never leave a shape's interior as bare background. No part of any object — fill, outline, highlight, gradient, shading or detail — may be `<PLATE HEX>` or any near-shade, tint or tone of it. No transparent, translucent, glassy, glowing or ghosted materials; every pixel of every object is fully opaque. No soft glows, blurs or drop shadows fading into the background. Keep the whole palette in strong contrast to `<PLATE HEX>`. The background must be visible ONLY around the outside of the objects, never showing through inside them.
3342
+ **What the prompt still has to ask for.** Three rules survive, and they're all about geometry rather than color:
3147
3343
 
3148
- `cutout --generate`, `sticker-pack --generate`, `vidfarm handoff image`, and the `create-overlay` REST primitive all append that clause automatically with the chosen plate hex substituted in **you only have to write it yourself when you're prompting a generator directly** (a free web tool by hand, or your own `POST /api/v1/primitives/images/generate` call). Do write it. It costs one paragraph and it is the difference between a usable pack and a re-generation.
3344
+ > Every object's OUTER EDGE must be a clearly different color from its own panel's background, and crisp no glow, blur, mist, drop shadow or reflection fading from the object into the background. Every enclosed area must be sealed by the artwork itself, not open to the background. Keep a clear margin of plain background between objects and around the sheet edge; nothing touching, overlapping or connected.
3149
3345
 
3150
- **And it's checked after the key.** `sticker-pack` and `cutout` measure, per item, the transparent area **fully enclosed by that item's own art** and report it as `hole_pct` (plus `holes`, and `hollow: true` at ≥20%) in `--json`, in `stickers.json`, and as a `⚠ N% hollow` flag on the console line. A ring, donut, picture frame or letter "O" reads the same way, so it **warns, never blocks** the judgement is yours. When items you expected to be solid come back flagged:
3346
+ `cutout --generate`, `sticker-pack --generate`, `vidfarm handoff image` and the `create-overlay` REST primitive append the right clause for you the relaxed one above for the smart keyer, and the strict "closed, solidly filled shapes, nothing in a near-plate shade, fully opaque" clause when `--key-mode flat` is in play. **Write it yourself only when you prompt a generator directly** (a free web tool by hand, or your own `POST /api/v1/primitives/images/generate` call).
3151
3347
 
3152
- - **Re-generate the sheet** with the fill clause above spelled out. This is the fix ~90% of the time.
3153
- - If the fill was merely *close* to the plate rather than absent, a lower `--tolerance` can rescue the sheet you already have. Nothing rescues a genuinely empty interior.
3154
- - Last resort for one stubborn item: `vidfarm mask ./sheet.png --crop x,y,w,h` — ONNX matting doesn't care what color the fill is, so it lifts art the chroma key can't.
3348
+ **The hollow check still runs, and it means something different now.** `sticker-pack` and `cutout` measure the transparent area **fully enclosed by an item's own art** and report `hole_pct` (plus `holes`, `hollow: true` at ≥20%) in `--json`, in `stickers.json`, and as a `⚠ N% hollow` console flag. Under the smart keyer an enclosed hole **can't** have been keyed away, so a flag is usually *real* art — a ring, donut, picture frame, letter "O", or an item drawn as separate pieces with background showing between them. It warns, never blocks. If a flagged item genuinely looks wrong, the shape's fill is **open** to the background through a gap in its outline; ask the prompt for sealed shapes. Last resort for one stubborn item: `vidfarm mask ./sheet.png --crop x,y,w,h` — ONNX matting doesn't care what color anything is.
3155
3349
 
3156
3350
  **Generation is the failure point, not the cutting.** The sheet prompt is auto-appended with the important instruction — *every item fully separated by clear plate-colored background, nothing touching or overlapping, wide margins, no text, no shadows, one consistent style* — because **touching items segment as one sticker**. If a pack comes back merged, re-run the generation asking for more spacing before you fight the `--gap` knob.
3157
3351