@officexapp/vidfarm-devcli 0.21.53 → 0.21.55

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.
@@ -382,7 +382,7 @@ You may be running as the **in-web AI chat** (the /editor copilot, the chat dock
382
382
  **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.)*
383
383
  - **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`.
384
384
  - **On devcli there's an OPTIONAL checker: `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). **Skipping it is fine — watching the render is the review that actually counts, and a clean `qa` is not one.** When you do run it, it allows **one** fix round by default: the first pass names the slop, one fix clears it, and a second round is nearly always taste rather than a defect. The human owns that number — `--max-revisions <n>` raises it, `0` disables it; ask rather than raising it yourself. `--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`.
385
- - **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 — **exactly five allowed families and no others**: TikTok Sans (safe default), Montserrat (bold default), Abel (condensed), Source Code Pro (mono, 700 only), Yesteryear (script accent line only) — at weight **700–900**, ~36–64px on a 1080-wide frame. Any other family (Inter / Roboto / Arial / system-ui / Georgia / a client brand font) is not imported and silently falls back to a web-default sans at render. **The regime on one page: <https://vidfarm.cc/fonts>** — all five rendered as real captions, the four legal backgrounds, and copy-paste commands. It 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").
385
+ - **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 — **five default families**: TikTok Sans (safe default), Montserrat (bold default), Abel (condensed), Source Code Pro (mono, 700 only), Yesteryear (script accent line only) — at weight **700–900**, ~36–64px on a 1080-wide frame. Any other family (Inter / Roboto / Arial / system-ui / Georgia / a client brand font) is not imported and silently falls back to a web-default sans at render — **so a custom font IS allowed, but only if you declare it in the composition** (`@font-face` or a Google Fonts `@import`); a declared family is never coerced, an undeclared one is. The regime is a heavy suggestion, and the fallback is the actual defect. **The regime on one page: <https://vidfarm.cc/fonts>** — all five rendered as real captions, the four legal backgrounds, and copy-paste commands. **Every family and background also has its own standalone reference image** (`https://vidfarm.cc/assets/fonts/caption-font-<family>.png`, e.g. `…caption-font-montserrat.png`; backgrounds are `…caption-bg-outline.png` and friends) — after you style a caption, pull a still and **compare it against the card for the family you picked**; a font that failed to load is obvious side by side and invisible on its own. It 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").
386
386
  - **Use the REAL track — a popular/copyrighted song in the render is expected here, not a problem.** Do not refuse it, do not silently substitute a generic royalty-free bed, and do not stop to warn about it. **Why it is legitimate:** the render is a **review artifact**, and at post time the user re-attaches the *same* song from the platform's own in-app music library (TikTok / Reels / Shorts), which is licensed through the platform's agreements with the labels. The music is therefore cleared on the surface where viewers actually hear it. **Why it is required:** a trending song IS the edit — cut points, pacing, the drop, the meme association. A render with the wrong bed, or no bed, reads as a different and much weaker video, so a review over a substitute bed grades a video nobody will ever post. Silent or generic-bed review is the bigger defect. **How to do it:** get the exact track (`vidfarm download-audio <post-url>`, the user's own file, or the audio off the source post the format is copying), and keep it on **its own `<audio>` layer** at its own `data-volume` — never baked into the footage or mixed into the voice stem. One separate track means the user can mute or swap it in one action when they upload, which is exactly what the platform flow expects. Say which track you used in the handoff. **The one limit:** this is for review renders and platform posting where the platform holds the license. If the user asks to sell, syndicate, or run the render as a **paid ad** with the track baked in, say once that ad placements are not covered by the in-app music license, and offer `vidfarm music "<same vibe, same BPM>"` as the swap for that cut. Then do what they decide.
387
387
  - **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.
388
388
  - **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.
@@ -439,10 +439,17 @@ Vidfarm keeps a small shelf of **experimental prompts**: complete, standalone me
439
439
 
440
440
  Why they exist: the prompts you find on the `/discover` pages are tuned to **one template's format**. These are deliberately **generalized** — the method, not the template — so they transfer to whatever you are building. The live index is `https://vidfarm.cc/experimental` (always current); today it holds:
441
441
 
442
+ **Two ways to reach any of them.** Fetch the URL, or read it **by name, offline** — every one of these ships inside the devcli package: `vidfarm harness list` prints this same shelf under *Format harnesses*, `vidfarm harness show <name>` prints one (`--dna <strand>` for a single strand), and `vidfarm qa ./work --harness <name>` grades a build against it. The name is the URL slug, and underscores resolve too — `meme_recaption` and `meme-recaption` are the same harness in both places. Prefer the CLI when you already have it: no fetch, no 402, and it stacks with a base (`--harness google-news-to-video --harness short-form`).
443
+
442
444
  | Prompt | What it does |
443
445
  |---|---|
444
446
  | `https://vidfarm.cc/experimental/unique-product-explainers.md` | N customer URLs → N product-introduction videos that do not look like each other. Differentiation as an input, frame-level review, measured verification |
445
447
  | `https://vidfarm.cc/experimental/google-news-to-video.md` | A recent real event → a timely video. Two stages: `news-search` finds the STORY, `video-search` finds the VISUALS. Query formulas, Google operators, licence discipline |
448
+ | `https://vidfarm.cc/experimental/meme-recaption.md` | One borrowed meme clip + one new caption aimed at the offer's problem space. Casting the meme for the caption's **verb**, the seven caption frames, naming the offer without letting the joke resolve into a pitch, keying a MemeScreens raw onto a background world, and a render-level QA gate. Runs at $0 |
449
+ | `https://vidfarm.cc/experimental/ugc-reaction-greenscreen.md` | Sell an app with three streams cut against each other: `ugc-reaction` raws, a Display Greenscreen device whose screen carries the customer's real demo, and the demo itself. One actor across every beat via `actor_<uuid>`, a per-frame tracked screen insert, captions inside the platform-chrome-safe core, and two exports from one render (voiceover-only to publish, voiceover+music to review) |
450
+ | `https://vidfarm.cc/experimental/sticker-slideshow-tips.md` | A tips **carousel** — the deliverable is N still slides, and the 3.0s-per-slide MP4 is only the playable preview. Die-cut cutouts on a paper page or a photo background, a literal "Tips for…" cover, the specificity ladder, one slide shilled from the middle and written so it survives deleting the brand name, four background modes, and a measured WCAG contrast gate on the exported PNGs |
451
+ | `https://vidfarm.cc/experimental/wall-text-pov-ugc.md` | One unbroken ambient take + one static block of unplated type. No cuts, no voiceover, no subtitles, nothing animated. The retention engine is arithmetic — `duration = words / 8`, so one play lands the viewer at the **halfway mark**, committed and one pass from done — floored at 8s so a trending sound gets a real phrase of a track. The four speaker frames, MIRROR vs TURN, a density pass that treats padding as the fatal failure, casting the scene dark so the type needs no plate (measured), ping-ponging the plate for a seamless loop, and three gates. Runs at $0 |
452
+ | `https://vidfarm.cc/experimental/animated-sticker-story.md` | A narrated **paper puppet theater** — one full-bleed parchment stage that never cuts, a cast of die-cut stickers, and every element moved by ONE paused GSAP timeline (MotionPathPlugin for walks; paths in absolute canvas coordinates, never `align:"self"`). The three-node rig, the seven moves (ENTER / WALK / CROWD / BEAT / STAMP / DRAW / CAMERA), buying SHEETS rather than stickers so one art class survives, generating art without shadows and adding one CSS drop-shadow, kinetic captions that animate **colour only** on whisper word timings, a two-pass build that MEASURES where the drawing is quietest before placing any type, a feathered paper wash that is not a plate, and the offer named once as a wordmark on the last beat. Desktop-only. ~$0.25 in `hybrid`, $0 in `minimize` |
446
453
 
447
454
  Fetch one as plain markdown and follow it end to end; do not skim it into a summary.
448
455
 
@@ -111,3 +111,29 @@ The `.harness/` directory a `vidfarm pull` writes is a different thing: machine-
111
111
  | `explainer` | Faceless educational video: one claim, invented visuals |
112
112
  | `product-demo` | Real product doing a real thing — the highest slop-risk format in the catalog |
113
113
  | `product-explainer` | Introducing a product a stranger has never heard of, with no usable screen footage. The plain-English line, the simple sticker-led open, per-client differentiation |
114
+
115
+ ## Format harnesses — the second shelf
116
+
117
+ `vidfarm harness list` prints the bases above **and** the format harnesses: complete contracts for
118
+ ONE format, published at `https://vidfarm.cc/experimental` and shipped inside the CLI package under
119
+ the same name as their URL slug. A base is a starting point you edit; a format harness is a method
120
+ you follow. Read one **before** you build that format, and stack it on a base.
121
+
122
+ | Name | For |
123
+ |---|---|
124
+ | `meme-recaption` | One borrowed meme clip, one new caption aimed at the offer's problem space |
125
+ | `wall-text-pov-ugc` | One unbroken ambient take + one static block of unplated type. `duration = words / 8` |
126
+ | `ugc-reaction-greenscreen` | Reaction cutaways + a keyed device carrying the customer's real app demo |
127
+ | `sticker-slideshow-tips` | A tips carousel — N still slides at 3.0s; the slides are the deliverable |
128
+ | `animated-sticker-story` | A narrated paper puppet theater on one parchment stage, one GSAP timeline |
129
+ | `google-news-to-video` | Timely newsjack — news-search finds the STORY, video-search the VISUALS |
130
+ | `unique-product-explainers` | N customer URLs → N videos that do not converge. Differentiation as an input |
131
+
132
+ ```bash
133
+ vidfarm harness list # both shelves
134
+ vidfarm harness show wall-text-pov-ugc # the whole contract, no fetch
135
+ vidfarm qa ./work --harness wall-text-pov-ugc --harness short-form # they STACK
136
+ ```
137
+
138
+ Underscores resolve too (`meme_recaption` = `meme-recaption`), on the CLI and at the URL. The web
139
+ index is the source of truth for what exists; `vidfarm harness list` is the same shelf, offline.
@@ -157,7 +157,7 @@ The test is the **native-editor test**: could you have made this element with th
157
157
 
158
158
  ### Rule 8 — the TikTok font regime, not the web's
159
159
 
160
- Captions and display type use the composition's bold font regime — **Montserrat (default) or TikTok Sans, weight 700–900, ~36–64px on a 1080-wide frame** (the full 5-family regime and the four legal backgrounds are on one page: <https://vidfarm.cc/fonts>), inside the **8%–85%** safe zone, placed in the emptiest part of the frame rather than dumped on the default lower third. **Web/Bootstrap type is the giveaway**: Inter / Roboto / Arial / system-ui at weight 400–600, thin light-grey subtitles, letter-spaced small caps. Matching the client's *brand* font is fine for a wordmark; it is not fine for the caption layer. Exactly one of four caption backgrounds: outline/stroke, plain + shadow, an active-word highlight pill, or a tight solid band (radius ≤8px). Caption colour and plate are **measured off the composited background**, one treatment for the whole video (`short-form.HARNESS.md` → "Caption styling is MEASURED off the background").
160
+ Captions and display type use the composition's bold font regime — **Montserrat (default) or TikTok Sans, weight 700–900, ~36–64px on a 1080-wide frame** (the full 5-family regime and the four legal backgrounds are on one page: <https://vidfarm.cc/fonts>, and each style has its own reference card at `https://vidfarm.cc/assets/fonts/caption-font-<family>.png` — compare a still of your frame against the card for the family you chose), inside the **8%–85%** safe zone, placed in the emptiest part of the frame rather than dumped on the default lower third. **Web/Bootstrap type is the giveaway**: Inter / Roboto / Arial / system-ui at weight 400–600, thin light-grey subtitles, letter-spaced small caps. Matching the client's *brand* font is fine for a wordmark; on the caption layer it is fine only if the composition actually ships that font (`@font-face` / Google Fonts `@import`) — an undeclared family falls back at render and gets coerced to Montserrat locally. Exactly one of four caption backgrounds: outline/stroke, plain + shadow, an active-word highlight pill, or a tight solid band (radius ≤8px). Caption colour and plate are **measured off the composited background**, one treatment for the whole video (`short-form.HARNESS.md` → "Caption styling is MEASURED off the background").
161
161
 
162
162
  ### Rule 9 — narration voiceover AND a music bed, both, always
163
163
 
@@ -114,7 +114,7 @@ The safe zone (8–85%) says where text is *allowed*; it does not say where text
114
114
 
115
115
  #### Caption styling is MEASURED off the background, never hardcoded
116
116
 
117
- > **Before you style anything, know the font regime.** A composition imports exactly **five** display families — TikTok Sans (safe default), Montserrat (bold default), Abel, Source Code Pro, Yesteryear — at weight **700–900**. Every other family (Inter, Roboto, Arial, Helvetica, system-ui, Georgia, a client's brand font) is **not imported** and silently falls back to a web-default sans at render, which is the loudest slop tell there is. The whole regime, each family rendered as a real caption, plus the four legal text backgrounds: **<https://vidfarm.cc/fonts>**. `font_regime: required` in the checks block above is this rule.
117
+ > **Before you style anything, know the font regime.** A composition imports **five** display families — TikTok Sans (safe default), Montserrat (bold default), Abel, Source Code Pro, Yesteryear — at weight **700–900**. Every other family (Inter, Roboto, Arial, Helvetica, system-ui, Georgia, a client's brand font) is **not imported** and silently falls back to a web-default sans at render, which is the loudest slop tell there is. A custom family is allowed if — and only if — you **declare it in the composition** (`@font-face` or a Google Fonts `@import`); the five are a heavy default, the silent fallback is the real defect. The whole regime, each family rendered as a real caption, plus the four legal text backgrounds: **<https://vidfarm.cc/fonts>**. Each style also has its own standalone card — `https://vidfarm.cc/assets/fonts/caption-font-<family>.png` (and `caption-bg-<style>.png`) — **open the card for the family you picked next to a still of your own frame and compare the glyphs.** `font_regime: required` in the checks block above is this rule.
118
118
 
119
119
  A white rounded caption plate copied from another video onto a near-black stage is a bright slab the design never asked for — it dominates the frame and reads as a UI element pasted over the video. So measure what is actually behind the caption band, then pick one of three treatments:
120
120
 
@@ -26,7 +26,8 @@ Two ways in, depending on where the format came from:
26
26
 
27
27
  ```bash
28
28
  # (a) From a bundled base — when the format is one you're defining
29
- vidfarm harness list # short-form | hooks | ugc-testimonial | explainer | product-demo | product-explainer
29
+ vidfarm harness list # bases: short-form | hooks | ugc-testimonial | explainer | product-demo | product-explainer
30
+ # + format harnesses: meme-recaption | wall-text-pov-ugc | … (the /experimental shelf, offline)
30
31
  vidfarm harness init hooks --out ./work/HARNESS.md
31
32
 
32
33
  # (b) From the template you're batching — when the format is one you're REPLICATING
@@ -62,7 +62,7 @@ A director who says "make me a video about X" usually wants the first. A directo
62
62
  | "give me the harness for this template_id" | `vidfarm harness derive <templateId\|forkId>` — the **decomposition**, as a harness |
63
63
 
64
64
  ```bash
65
- vidfarm harness list # the bundled starting points
65
+ vidfarm harness list # bases to edit + the format harnesses (vidfarm.cc/experimental, offline)
66
66
  vidfarm harness init short-form --out ./work/HARNESS.md # copy, then EDIT it
67
67
  vidfarm harness derive <forkId> --out ./work/HARNESS.md # a decomposed template → a harness
68
68
  vidfarm harness show ./work/HARNESS.md --dna visual # ONE strand, not the whole doc
@@ -84,7 +84,7 @@ vidfarm qa ./work --harness hooks --harness ./brand/HOUSE.md # built-in + your o
84
84
 
85
85
  > 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.
86
86
 
87
- 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`**, **`product-explainer`** (introducing a brand nobody has heard of, with no usable screen footage: the plain-English line by t=5s, the ≤3-text-run sticker-led open, VO + music bed, and the assignment method that stops N client videos converging). 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.
87
+ `vidfarm harness list` prints **two shelves**. The second one is the format harnesses — the `https://vidfarm.cc/experimental` shelf, shipped in the package so `vidfarm harness show meme-recaption` works with no fetch. Those are complete contracts for ONE format: read one before you build that format, and stack it on a base (`--harness wall-text-pov-ugc --harness short-form`). The first shelf is the bundled bases (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`**, **`product-explainer`** (introducing a brand nobody has heard of, with no usable screen footage: the plain-English line by t=5s, the ≤3-text-run sticker-led open, VO + music bed, and the assignment method that stops N client videos converging). 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.
88
88
 
89
89
  **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.
90
90
 
@@ -366,7 +366,7 @@ What it flags:
366
366
  | `clickable-element` | error/warn | `<button>`, `<form>`, `<input>`; `<a href>` warns |
367
367
  | `web-framework-classes` | error/warn | Bootstrap/Tailwind class tokens (`btn`, `badge`, `card`, `hero`, `col-*`, `rounded-full`, `shadow-lg`, `backdrop-blur`, `bg-gradient-to-*`) or a linked CSS framework. A `<script>` CDN for GSAP/anime.js is fine |
368
368
  | `page-structure` / `bullet-list` | error/warn | `<nav>`/`<header>`/`<footer>`/`<table>`; a `<ul>` with visible bullet markers |
369
- | `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) |
369
+ | `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). **A custom family the composition DECLARES** (`@font-face` or a Google Fonts `@import` naming it) **is not flagged** — the rule targets the silent fallback, not your typography. Each regime family has a reference card at `https://vidfarm.cc/assets/fonts/caption-font-<family>.png`; compare a still against it after styling |
370
370
  | `font-size` / `font-weight` | error/warn | `font-size:0` (invisible) is an error; sub-2.6%-of-canvas-width text and weight <600 warn |
371
371
  | `caption-safe-zone` | warn | Text outside the 8%–85% band on a **portrait** canvas (landscape/square are exempt) |
372
372
  | `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 |
@@ -53,6 +53,21 @@ Templates are listed on the Vidfarm homepage and `/discover`. Each has a `templa
53
53
  - API: `GET /discover/feed` — returns `{ templates: [{ templateId, slugId, title, previewUrl, viralDna, durationSeconds, sourceType, promotions, keywords, summary, ... }], next_cursor }`
54
54
  - Search: `GET /discover/feed?q=<offer>&limit=20&sort=relevance` hybrid-searches the eligible public catalog using semantic embeddings plus lexical matches. Semantic query embedding uses Vidfarm's canonical OpenRouter-routed model and bills the provider cost × the standard 1.2 markup to the user's wallet. The response's `search` block reports `mode`, `embedding_space`, and any `semantic_limitation`; when it says `lexical_structured`, disclose the limitation briefly and continue rather than refusing. Decomposition adds `promotions`, `keywords`, `summary`, `categoryTags`, and `catalogIntelligence` (`wowScore`, `wowReason`, `automationScore`, `automationReason`, `contentStyles`, `searchText`). Use `sort=wow` for highest-quality/client-impressing formats, `sort=automation` for cheap repeatable bulk formats, and `sort=recent` only when freshness is the intent. Follow `next_cursor` with `cursor=<value>`; never call page one the whole catalog. `GET /api/v1/videos?q=<offer>&limit=20[&mine=true]` searches source **inspirations**. Undecomposed inspirations have only sparse ingest metadata, so they are harder to retrieve semantically. Explain that somebody in the world needs to decompose one once and the shared enrichment then benefits everyone; the current user need not act unless they want that specific inspiration immediately.
55
55
 
56
+ ### Featured templates (members only)
57
+
58
+ Featured is the small, hand-curated shelf at the top of the Discover feed. Signed-in members open on it by default; anonymous visitors do not see it at all.
59
+
60
+ - `GET /discover/feed?view=featured` — the shelf, in curated order (`featured: true`, `featuredRank` ascending). Signed-out callers get `401`.
61
+ - In the ordinary `view=available` feed, a signed-in caller's featured picks sort first (an active `q=` search keeps relevance order instead).
62
+ - Browser: the **View** dropdown on `/discover/templates/feed` carries **View Featured**, and `?view=featured` deep-links to it.
63
+
64
+ Operators curate the shelf with the superagency key (`x-superagency-key`). Every id below accepts an `inspiration_...`, `template_...`, or `fork_...` id:
65
+
66
+ - `GET /api/v1/admin/discover/featured` — the current shelf, plus `unresolved` ids that no longer render (deleted, archived, or gone private).
67
+ - `POST /api/v1/admin/discover/featured { id, rank? }` — feature one template. Default rank appends it to the end.
68
+ - `PUT /api/v1/admin/discover/featured { items: [{ id, rank? }] }` — replace the whole shelf; array order is shelf order, and anything not listed is un-featured. Ids that resolve to nothing come back under `unknown`.
69
+ - `DELETE /api/v1/admin/discover/featured/:id` — drop one pick from the shelf.
70
+
56
71
  Each template exposes a public preview:
57
72
 
58
73
  - `GET /editor/:templateId` — opens the Trackpad Editor for the template (redirects to your fork of it, or to `/login`)
@@ -616,18 +616,24 @@ Short-form is watched on a phone, and the phone's UI eats the frame's edges. **N
616
616
  - *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.
617
617
  - *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.
618
618
  - **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.
619
- - **Font → the composition regime. Five families, no others.** The whole regime on one page — each family rendered as a real caption, the four legal backgrounds, copy-paste `set-style` commands: **<https://vidfarm.cc/fonts>** (specimen image alone: `https://vidfarm.cc/assets/tiktok-caption-fonts.png`). Read it once before you style anything, and hand the link to a human director who is picking a look.
620
-
621
- | Family | Weights it really has | Use it for |
622
- |---|---|---|
623
- | **TikTok Sans** | 400 / 600 / 700 / 800 / 900 | The native TikTok caption look. **The safe default when unsure.** |
624
- | **Montserrat** | 600 / 700 / 800 / 900 | Geometric bold display. Hooks, hard statements, the Hormozi caption. **The bold default.** |
625
- | **Abel** | 400 only | Condensed headline / newsletter vibe. A long line that must stay on one row. |
626
- | **Source Code Pro** | 700 only | Code / terminal beats only. Never a whole video. |
627
- | **Yesteryear** | 400 only | Cursive script. **One accent line** (a quote) never a caption track; it is unreadable at cue size. |
628
- | ~~Georgia~~ | | **Decompose-only. Never author in it.** The decompose vision pass may report `Georgia` off a source video's serif (it is the 6th value in `ALLOWED_FONTS`, `src/services/hyperframes.ts`), but the composition does not import it and `normalizeTikTokCaptionLayout` coerces it to Montserrat on every local render. Rebuild an editorial look in Abel or Montserrat instead. |
629
-
630
- These five are exactly what the composition `@import`s from Google Fonts, and exactly what `CAPTION_FONT_REGIME` (`src/devcli/composition-edit.ts`) keeps. **Anything else is not imported**: `Inter`, `Roboto`, `Arial`, `Helvetica`, `system-ui`, a client's brand font the render silently falls back to a web-default sans, which is exactly the slop look. Matching a client brand font is fine for a *wordmark image*; it is never fine for the caption layer. Asking for a weight the family does not ship (Abel 900, Yesteryear 700) fakes it with a synthetic bold and looks smeared pick a family that has the weight instead. `vidfarm qa-check` flags off-regime families (`font-regime` rule).
619
+ - **Font → the composition regime. Five families are the heavy default.** The whole regime on one page — each family rendered as a real caption, the four legal backgrounds, copy-paste `set-style` commands: **<https://vidfarm.cc/fonts>** (combined specimen sheet: `https://vidfarm.cc/assets/tiktok-caption-fonts.png`). Read it once before you style anything, and hand the link to a human director who is picking a look.
620
+
621
+ **Every style also has its OWN standalone reference image** — one family (or one background), rendered at caption size on real footage-like plate. **Use them for comparison.** The workflow: pick a family → style the layer → pull a still (`vidfarm stills ./work --at <t>`) → open that family's card next to the still and check the glyphs match. A fallback font is obvious side by side and nearly invisible on its own, and this is the only check that catches a family that failed to load. Fetch the one card you need instead of the whole sheet.
622
+
623
+ | Family | Weights it really has | Standalone reference card | Use it for |
624
+ |---|---|---|---|
625
+ | **TikTok Sans** | 400 / 600 / 700 / 800 / 900 | `https://vidfarm.cc/assets/fonts/caption-font-tiktok-sans.png` | The native TikTok caption look. **The safe default when unsure.** |
626
+ | **Montserrat** | 600 / 700 / 800 / 900 | `https://vidfarm.cc/assets/fonts/caption-font-montserrat.png` | Geometric bold display. Hooks, hard statements, the Hormozi caption. **The bold default.** |
627
+ | **Abel** | 400 only | `https://vidfarm.cc/assets/fonts/caption-font-abel.png` | Condensed headline / newsletter vibe. A long line that must stay on one row. |
628
+ | **Source Code Pro** | 700 only | `https://vidfarm.cc/assets/fonts/caption-font-source-code-pro.png` | Code / terminal beats only. Never a whole video. |
629
+ | **Yesteryear** | 400 only | `https://vidfarm.cc/assets/fonts/caption-font-yesteryear.png` | Cursive script. **One accent line** (a quote) — never a caption track; it is unreadable at cue size. |
630
+ | ~~Georgia~~ | | `https://vidfarm.cc/assets/fonts/caption-font-georgia.png` (anti-example) | **Decompose-only. Do not author in it.** The decompose vision pass may report `Georgia` off a source video's serif (it is the 6th value in `ALLOWED_FONTS`, `src/services/hyperframes.ts`), but the composition does not import it, so `normalizeTikTokCaptionLayout` coerces it to Montserrat on every local render. Rebuild an editorial look in Abel or Montserrat instead. |
631
+
632
+ The four legal backgrounds have cards too: `caption-bg-outline.png`, `caption-bg-plain.png`, `caption-bg-spotlight.png`, `caption-bg-highlight-solid.png` (same `/assets/fonts/` path). Regenerate all ten with `node scripts/render-font-specimens.mjs`.
633
+
634
+ These five are exactly what the composition `@import`s from Google Fonts, and exactly what `CAPTION_FONT_REGIME` (`src/devcli/composition-edit.ts`) keeps. **Anything else is normally not imported**: `Inter`, `Roboto`, `Arial`, `Helvetica`, `system-ui`, a client's brand font — the render silently falls back to a web-default sans, which is exactly the slop look. Asking for a weight the family does not ship (Abel 900, Yesteryear 700) fakes it with a synthetic bold and looks smeared — pick a family that has the weight instead.
635
+
636
+ **Custom fonts are allowed — the regime is a heavy suggestion, not a ban.** What is forbidden is *naming* a family the composition never ships, because that one silently falls back. If you want a sixth family, **declare it in the composition**: an `@font-face` pointing at a real file, or a Google Fonts `@import`/`<link>` that names it. A declared family is left alone — `normalizeTikTokCaptionLayout` does not coerce it and `vidfarm qa` does not flag it (`compositionDeclaresFont`, `src/devcli/composition-edit.ts`). An undeclared one is coerced to Montserrat locally and reported as a `font-regime` finding. Deviate on purpose, ship the font, then confirm on a rendered still — the editor preview loads fonts your render machine may not have. Matching a client's brand font is always fine for a *wordmark image*; on the caption layer, only do it with the `@font-face` in place.
631
637
  - **Background → one of exactly four valid treatments.** Any text you place uses one of these and nothing else:
632
638
 
633
639
  | # | Treatment | How to set it | When |
@@ -652,7 +658,7 @@ Short-form is watched on a phone, and the phone's UI eats the frame's edges. **N
652
658
 
653
659
  **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.
654
660
 
655
- > 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.
661
+ > 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 — **unless the composition declares that family itself**, in which case your custom font renders as authored. 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.
656
662
 
657
663
  ### Animated captions — word-by-word caption styles (TikTok/CapCut)
658
664
 
@@ -39,6 +39,7 @@ Then answer these, out loud, in your report:
39
39
  - **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.
40
40
  - **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`).
41
41
  - **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.
42
+ - **Did the font you asked for actually render?** A family the composition never imported falls back to a web-default sans silently — the render *looks* fine, just generic, and you will not notice by memory. Open the standalone reference card for the family you chose (`https://vidfarm.cc/assets/fonts/caption-font-<family>.png` — `tiktok-sans`, `montserrat`, `abel`, `source-code-pro`, `yesteryear`, plus the `georgia` anti-example) next to your still and **compare the glyphs**. Same check for the text background: `caption-bg-outline|plain|spotlight|highlight-solid.png`. A fallback is obvious side by side and invisible on its own.
42
43
  - **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.
43
44
  - **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.
44
45
  - **Rhythm and pacing.** Do scene durations form a deliberate pattern (a fast open, a longer explanation, a fast close), or is every scene the same length because a loop wrote them? Same-length beats are hypnotic in the bad way. Conversely, one 9-second hold in a video of 2-second cuts stalls it dead.
package/SKILL.director.md CHANGED
@@ -382,7 +382,7 @@ You may be running as the **in-web AI chat** (the /editor copilot, the chat dock
382
382
  **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.)*
383
383
  - **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`.
384
384
  - **On devcli there's an OPTIONAL checker: `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). **Skipping it is fine — watching the render is the review that actually counts, and a clean `qa` is not one.** When you do run it, it allows **one** fix round by default: the first pass names the slop, one fix clears it, and a second round is nearly always taste rather than a defect. The human owns that number — `--max-revisions <n>` raises it, `0` disables it; ask rather than raising it yourself. `--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`.
385
- - **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 — **exactly five allowed families and no others**: TikTok Sans (safe default), Montserrat (bold default), Abel (condensed), Source Code Pro (mono, 700 only), Yesteryear (script accent line only) — at weight **700–900**, ~36–64px on a 1080-wide frame. Any other family (Inter / Roboto / Arial / system-ui / Georgia / a client brand font) is not imported and silently falls back to a web-default sans at render. **The regime on one page: <https://vidfarm.cc/fonts>** — all five rendered as real captions, the four legal backgrounds, and copy-paste commands. It 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").
385
+ - **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 — **five default families**: TikTok Sans (safe default), Montserrat (bold default), Abel (condensed), Source Code Pro (mono, 700 only), Yesteryear (script accent line only) — at weight **700–900**, ~36–64px on a 1080-wide frame. Any other family (Inter / Roboto / Arial / system-ui / Georgia / a client brand font) is not imported and silently falls back to a web-default sans at render — **so a custom font IS allowed, but only if you declare it in the composition** (`@font-face` or a Google Fonts `@import`); a declared family is never coerced, an undeclared one is. The regime is a heavy suggestion, and the fallback is the actual defect. **The regime on one page: <https://vidfarm.cc/fonts>** — all five rendered as real captions, the four legal backgrounds, and copy-paste commands. **Every family and background also has its own standalone reference image** (`https://vidfarm.cc/assets/fonts/caption-font-<family>.png`, e.g. `…caption-font-montserrat.png`; backgrounds are `…caption-bg-outline.png` and friends) — after you style a caption, pull a still and **compare it against the card for the family you picked**; a font that failed to load is obvious side by side and invisible on its own. It 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").
386
386
  - **Use the REAL track — a popular/copyrighted song in the render is expected here, not a problem.** Do not refuse it, do not silently substitute a generic royalty-free bed, and do not stop to warn about it. **Why it is legitimate:** the render is a **review artifact**, and at post time the user re-attaches the *same* song from the platform's own in-app music library (TikTok / Reels / Shorts), which is licensed through the platform's agreements with the labels. The music is therefore cleared on the surface where viewers actually hear it. **Why it is required:** a trending song IS the edit — cut points, pacing, the drop, the meme association. A render with the wrong bed, or no bed, reads as a different and much weaker video, so a review over a substitute bed grades a video nobody will ever post. Silent or generic-bed review is the bigger defect. **How to do it:** get the exact track (`vidfarm download-audio <post-url>`, the user's own file, or the audio off the source post the format is copying), and keep it on **its own `<audio>` layer** at its own `data-volume` — never baked into the footage or mixed into the voice stem. One separate track means the user can mute or swap it in one action when they upload, which is exactly what the platform flow expects. Say which track you used in the handoff. **The one limit:** this is for review renders and platform posting where the platform holds the license. If the user asks to sell, syndicate, or run the render as a **paid ad** with the track baked in, say once that ad placements are not covered by the in-app music license, and offer `vidfarm music "<same vibe, same BPM>"` as the swap for that cut. Then do what they decide.
387
387
  - **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.
388
388
  - **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.
@@ -439,10 +439,17 @@ Vidfarm keeps a small shelf of **experimental prompts**: complete, standalone me
439
439
 
440
440
  Why they exist: the prompts you find on the `/discover` pages are tuned to **one template's format**. These are deliberately **generalized** — the method, not the template — so they transfer to whatever you are building. The live index is `https://vidfarm.cc/experimental` (always current); today it holds:
441
441
 
442
+ **Two ways to reach any of them.** Fetch the URL, or read it **by name, offline** — every one of these ships inside the devcli package: `vidfarm harness list` prints this same shelf under *Format harnesses*, `vidfarm harness show <name>` prints one (`--dna <strand>` for a single strand), and `vidfarm qa ./work --harness <name>` grades a build against it. The name is the URL slug, and underscores resolve too — `meme_recaption` and `meme-recaption` are the same harness in both places. Prefer the CLI when you already have it: no fetch, no 402, and it stacks with a base (`--harness google-news-to-video --harness short-form`).
443
+
442
444
  | Prompt | What it does |
443
445
  |---|---|
444
446
  | `https://vidfarm.cc/experimental/unique-product-explainers.md` | N customer URLs → N product-introduction videos that do not look like each other. Differentiation as an input, frame-level review, measured verification |
445
447
  | `https://vidfarm.cc/experimental/google-news-to-video.md` | A recent real event → a timely video. Two stages: `news-search` finds the STORY, `video-search` finds the VISUALS. Query formulas, Google operators, licence discipline |
448
+ | `https://vidfarm.cc/experimental/meme-recaption.md` | One borrowed meme clip + one new caption aimed at the offer's problem space. Casting the meme for the caption's **verb**, the seven caption frames, naming the offer without letting the joke resolve into a pitch, keying a MemeScreens raw onto a background world, and a render-level QA gate. Runs at $0 |
449
+ | `https://vidfarm.cc/experimental/ugc-reaction-greenscreen.md` | Sell an app with three streams cut against each other: `ugc-reaction` raws, a Display Greenscreen device whose screen carries the customer's real demo, and the demo itself. One actor across every beat via `actor_<uuid>`, a per-frame tracked screen insert, captions inside the platform-chrome-safe core, and two exports from one render (voiceover-only to publish, voiceover+music to review) |
450
+ | `https://vidfarm.cc/experimental/sticker-slideshow-tips.md` | A tips **carousel** — the deliverable is N still slides, and the 3.0s-per-slide MP4 is only the playable preview. Die-cut cutouts on a paper page or a photo background, a literal "Tips for…" cover, the specificity ladder, one slide shilled from the middle and written so it survives deleting the brand name, four background modes, and a measured WCAG contrast gate on the exported PNGs |
451
+ | `https://vidfarm.cc/experimental/wall-text-pov-ugc.md` | One unbroken ambient take + one static block of unplated type. No cuts, no voiceover, no subtitles, nothing animated. The retention engine is arithmetic — `duration = words / 8`, so one play lands the viewer at the **halfway mark**, committed and one pass from done — floored at 8s so a trending sound gets a real phrase of a track. The four speaker frames, MIRROR vs TURN, a density pass that treats padding as the fatal failure, casting the scene dark so the type needs no plate (measured), ping-ponging the plate for a seamless loop, and three gates. Runs at $0 |
452
+ | `https://vidfarm.cc/experimental/animated-sticker-story.md` | A narrated **paper puppet theater** — one full-bleed parchment stage that never cuts, a cast of die-cut stickers, and every element moved by ONE paused GSAP timeline (MotionPathPlugin for walks; paths in absolute canvas coordinates, never `align:"self"`). The three-node rig, the seven moves (ENTER / WALK / CROWD / BEAT / STAMP / DRAW / CAMERA), buying SHEETS rather than stickers so one art class survives, generating art without shadows and adding one CSS drop-shadow, kinetic captions that animate **colour only** on whisper word timings, a two-pass build that MEASURES where the drawing is quietest before placing any type, a feathered paper wash that is not a plate, and the offer named once as a wordmark on the last beat. Desktop-only. ~$0.25 in `hybrid`, $0 in `minimize` |
446
453
 
447
454
  Fetch one as plain markdown and follow it end to end; do not skim it into a summary.
448
455
 
@@ -558,6 +565,21 @@ Templates are listed on the Vidfarm homepage and `/discover`. Each has a `templa
558
565
  - API: `GET /discover/feed` — returns `{ templates: [{ templateId, slugId, title, previewUrl, viralDna, durationSeconds, sourceType, promotions, keywords, summary, ... }], next_cursor }`
559
566
  - Search: `GET /discover/feed?q=<offer>&limit=20&sort=relevance` hybrid-searches the eligible public catalog using semantic embeddings plus lexical matches. Semantic query embedding uses Vidfarm's canonical OpenRouter-routed model and bills the provider cost × the standard 1.2 markup to the user's wallet. The response's `search` block reports `mode`, `embedding_space`, and any `semantic_limitation`; when it says `lexical_structured`, disclose the limitation briefly and continue rather than refusing. Decomposition adds `promotions`, `keywords`, `summary`, `categoryTags`, and `catalogIntelligence` (`wowScore`, `wowReason`, `automationScore`, `automationReason`, `contentStyles`, `searchText`). Use `sort=wow` for highest-quality/client-impressing formats, `sort=automation` for cheap repeatable bulk formats, and `sort=recent` only when freshness is the intent. Follow `next_cursor` with `cursor=<value>`; never call page one the whole catalog. `GET /api/v1/videos?q=<offer>&limit=20[&mine=true]` searches source **inspirations**. Undecomposed inspirations have only sparse ingest metadata, so they are harder to retrieve semantically. Explain that somebody in the world needs to decompose one once and the shared enrichment then benefits everyone; the current user need not act unless they want that specific inspiration immediately.
560
567
 
568
+ ### Featured templates (members only)
569
+
570
+ Featured is the small, hand-curated shelf at the top of the Discover feed. Signed-in members open on it by default; anonymous visitors do not see it at all.
571
+
572
+ - `GET /discover/feed?view=featured` — the shelf, in curated order (`featured: true`, `featuredRank` ascending). Signed-out callers get `401`.
573
+ - In the ordinary `view=available` feed, a signed-in caller's featured picks sort first (an active `q=` search keeps relevance order instead).
574
+ - Browser: the **View** dropdown on `/discover/templates/feed` carries **View Featured**, and `?view=featured` deep-links to it.
575
+
576
+ Operators curate the shelf with the superagency key (`x-superagency-key`). Every id below accepts an `inspiration_...`, `template_...`, or `fork_...` id:
577
+
578
+ - `GET /api/v1/admin/discover/featured` — the current shelf, plus `unresolved` ids that no longer render (deleted, archived, or gone private).
579
+ - `POST /api/v1/admin/discover/featured { id, rank? }` — feature one template. Default rank appends it to the end.
580
+ - `PUT /api/v1/admin/discover/featured { items: [{ id, rank? }] }` — replace the whole shelf; array order is shelf order, and anything not listed is un-featured. Ids that resolve to nothing come back under `unknown`.
581
+ - `DELETE /api/v1/admin/discover/featured/:id` — drop one pick from the shelf.
582
+
561
583
  Each template exposes a public preview:
562
584
 
563
585
  - `GET /editor/:templateId` — opens the Trackpad Editor for the template (redirects to your fork of it, or to `/login`)
@@ -1645,18 +1667,24 @@ Short-form is watched on a phone, and the phone's UI eats the frame's edges. **N
1645
1667
  - *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.
1646
1668
  - *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.
1647
1669
  - **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.
1648
- - **Font → the composition regime. Five families, no others.** The whole regime on one page — each family rendered as a real caption, the four legal backgrounds, copy-paste `set-style` commands: **<https://vidfarm.cc/fonts>** (specimen image alone: `https://vidfarm.cc/assets/tiktok-caption-fonts.png`). Read it once before you style anything, and hand the link to a human director who is picking a look.
1649
-
1650
- | Family | Weights it really has | Use it for |
1651
- |---|---|---|
1652
- | **TikTok Sans** | 400 / 600 / 700 / 800 / 900 | The native TikTok caption look. **The safe default when unsure.** |
1653
- | **Montserrat** | 600 / 700 / 800 / 900 | Geometric bold display. Hooks, hard statements, the Hormozi caption. **The bold default.** |
1654
- | **Abel** | 400 only | Condensed headline / newsletter vibe. A long line that must stay on one row. |
1655
- | **Source Code Pro** | 700 only | Code / terminal beats only. Never a whole video. |
1656
- | **Yesteryear** | 400 only | Cursive script. **One accent line** (a quote) never a caption track; it is unreadable at cue size. |
1657
- | ~~Georgia~~ | | **Decompose-only. Never author in it.** The decompose vision pass may report `Georgia` off a source video's serif (it is the 6th value in `ALLOWED_FONTS`, `src/services/hyperframes.ts`), but the composition does not import it and `normalizeTikTokCaptionLayout` coerces it to Montserrat on every local render. Rebuild an editorial look in Abel or Montserrat instead. |
1658
-
1659
- These five are exactly what the composition `@import`s from Google Fonts, and exactly what `CAPTION_FONT_REGIME` (`src/devcli/composition-edit.ts`) keeps. **Anything else is not imported**: `Inter`, `Roboto`, `Arial`, `Helvetica`, `system-ui`, a client's brand font the render silently falls back to a web-default sans, which is exactly the slop look. Matching a client brand font is fine for a *wordmark image*; it is never fine for the caption layer. Asking for a weight the family does not ship (Abel 900, Yesteryear 700) fakes it with a synthetic bold and looks smeared pick a family that has the weight instead. `vidfarm qa-check` flags off-regime families (`font-regime` rule).
1670
+ - **Font → the composition regime. Five families are the heavy default.** The whole regime on one page — each family rendered as a real caption, the four legal backgrounds, copy-paste `set-style` commands: **<https://vidfarm.cc/fonts>** (combined specimen sheet: `https://vidfarm.cc/assets/tiktok-caption-fonts.png`). Read it once before you style anything, and hand the link to a human director who is picking a look.
1671
+
1672
+ **Every style also has its OWN standalone reference image** — one family (or one background), rendered at caption size on real footage-like plate. **Use them for comparison.** The workflow: pick a family → style the layer → pull a still (`vidfarm stills ./work --at <t>`) → open that family's card next to the still and check the glyphs match. A fallback font is obvious side by side and nearly invisible on its own, and this is the only check that catches a family that failed to load. Fetch the one card you need instead of the whole sheet.
1673
+
1674
+ | Family | Weights it really has | Standalone reference card | Use it for |
1675
+ |---|---|---|---|
1676
+ | **TikTok Sans** | 400 / 600 / 700 / 800 / 900 | `https://vidfarm.cc/assets/fonts/caption-font-tiktok-sans.png` | The native TikTok caption look. **The safe default when unsure.** |
1677
+ | **Montserrat** | 600 / 700 / 800 / 900 | `https://vidfarm.cc/assets/fonts/caption-font-montserrat.png` | Geometric bold display. Hooks, hard statements, the Hormozi caption. **The bold default.** |
1678
+ | **Abel** | 400 only | `https://vidfarm.cc/assets/fonts/caption-font-abel.png` | Condensed headline / newsletter vibe. A long line that must stay on one row. |
1679
+ | **Source Code Pro** | 700 only | `https://vidfarm.cc/assets/fonts/caption-font-source-code-pro.png` | Code / terminal beats only. Never a whole video. |
1680
+ | **Yesteryear** | 400 only | `https://vidfarm.cc/assets/fonts/caption-font-yesteryear.png` | Cursive script. **One accent line** (a quote) — never a caption track; it is unreadable at cue size. |
1681
+ | ~~Georgia~~ | | `https://vidfarm.cc/assets/fonts/caption-font-georgia.png` (anti-example) | **Decompose-only. Do not author in it.** The decompose vision pass may report `Georgia` off a source video's serif (it is the 6th value in `ALLOWED_FONTS`, `src/services/hyperframes.ts`), but the composition does not import it, so `normalizeTikTokCaptionLayout` coerces it to Montserrat on every local render. Rebuild an editorial look in Abel or Montserrat instead. |
1682
+
1683
+ The four legal backgrounds have cards too: `caption-bg-outline.png`, `caption-bg-plain.png`, `caption-bg-spotlight.png`, `caption-bg-highlight-solid.png` (same `/assets/fonts/` path). Regenerate all ten with `node scripts/render-font-specimens.mjs`.
1684
+
1685
+ These five are exactly what the composition `@import`s from Google Fonts, and exactly what `CAPTION_FONT_REGIME` (`src/devcli/composition-edit.ts`) keeps. **Anything else is normally not imported**: `Inter`, `Roboto`, `Arial`, `Helvetica`, `system-ui`, a client's brand font — the render silently falls back to a web-default sans, which is exactly the slop look. Asking for a weight the family does not ship (Abel 900, Yesteryear 700) fakes it with a synthetic bold and looks smeared — pick a family that has the weight instead.
1686
+
1687
+ **Custom fonts are allowed — the regime is a heavy suggestion, not a ban.** What is forbidden is *naming* a family the composition never ships, because that one silently falls back. If you want a sixth family, **declare it in the composition**: an `@font-face` pointing at a real file, or a Google Fonts `@import`/`<link>` that names it. A declared family is left alone — `normalizeTikTokCaptionLayout` does not coerce it and `vidfarm qa` does not flag it (`compositionDeclaresFont`, `src/devcli/composition-edit.ts`). An undeclared one is coerced to Montserrat locally and reported as a `font-regime` finding. Deviate on purpose, ship the font, then confirm on a rendered still — the editor preview loads fonts your render machine may not have. Matching a client's brand font is always fine for a *wordmark image*; on the caption layer, only do it with the `@font-face` in place.
1660
1688
  - **Background → one of exactly four valid treatments.** Any text you place uses one of these and nothing else:
1661
1689
 
1662
1690
  | # | Treatment | How to set it | When |
@@ -1681,7 +1709,7 @@ Short-form is watched on a phone, and the phone's UI eats the frame's edges. **N
1681
1709
 
1682
1710
  **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.
1683
1711
 
1684
- > 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.
1712
+ > 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 — **unless the composition declares that family itself**, in which case your custom font renders as authored. 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.
1685
1713
 
1686
1714
  ### Animated captions — word-by-word caption styles (TikTok/CapCut)
1687
1715
 
@@ -2114,6 +2142,7 @@ Then answer these, out loud, in your report:
2114
2142
  - **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.
2115
2143
  - **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`).
2116
2144
  - **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.
2145
+ - **Did the font you asked for actually render?** A family the composition never imported falls back to a web-default sans silently — the render *looks* fine, just generic, and you will not notice by memory. Open the standalone reference card for the family you chose (`https://vidfarm.cc/assets/fonts/caption-font-<family>.png` — `tiktok-sans`, `montserrat`, `abel`, `source-code-pro`, `yesteryear`, plus the `georgia` anti-example) next to your still and **compare the glyphs**. Same check for the text background: `caption-bg-outline|plain|spotlight|highlight-solid.png`. A fallback is obvious side by side and invisible on its own.
2117
2146
  - **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.
2118
2147
  - **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.
2119
2148
  - **Rhythm and pacing.** Do scene durations form a deliberate pattern (a fast open, a longer explanation, a fast close), or is every scene the same length because a loop wrote them? Same-length beats are hypnotic in the bad way. Conversely, one 9-second hold in a video of 2-second cuts stalls it dead.
@@ -2881,7 +2910,7 @@ A director who says "make me a video about X" usually wants the first. A directo
2881
2910
  | "give me the harness for this template_id" | `vidfarm harness derive <templateId\|forkId>` — the **decomposition**, as a harness |
2882
2911
 
2883
2912
  ```bash
2884
- vidfarm harness list # the bundled starting points
2913
+ vidfarm harness list # bases to edit + the format harnesses (vidfarm.cc/experimental, offline)
2885
2914
  vidfarm harness init short-form --out ./work/HARNESS.md # copy, then EDIT it
2886
2915
  vidfarm harness derive <forkId> --out ./work/HARNESS.md # a decomposed template → a harness
2887
2916
  vidfarm harness show ./work/HARNESS.md --dna visual # ONE strand, not the whole doc
@@ -2903,7 +2932,7 @@ vidfarm qa ./work --harness hooks --harness ./brand/HOUSE.md # built-in + your o
2903
2932
 
2904
2933
  > 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.
2905
2934
 
2906
- 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`**, **`product-explainer`** (introducing a brand nobody has heard of, with no usable screen footage: the plain-English line by t=5s, the ≤3-text-run sticker-led open, VO + music bed, and the assignment method that stops N client videos converging). 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.
2935
+ `vidfarm harness list` prints **two shelves**. The second one is the format harnesses — the `https://vidfarm.cc/experimental` shelf, shipped in the package so `vidfarm harness show meme-recaption` works with no fetch. Those are complete contracts for ONE format: read one before you build that format, and stack it on a base (`--harness wall-text-pov-ugc --harness short-form`). The first shelf is the bundled bases (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`**, **`product-explainer`** (introducing a brand nobody has heard of, with no usable screen footage: the plain-English line by t=5s, the ≤3-text-run sticker-led open, VO + music bed, and the assignment method that stops N client videos converging). 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.
2907
2936
 
2908
2937
  **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.
2909
2938
 
@@ -3185,7 +3214,7 @@ What it flags:
3185
3214
  | `clickable-element` | error/warn | `<button>`, `<form>`, `<input>`; `<a href>` warns |
3186
3215
  | `web-framework-classes` | error/warn | Bootstrap/Tailwind class tokens (`btn`, `badge`, `card`, `hero`, `col-*`, `rounded-full`, `shadow-lg`, `backdrop-blur`, `bg-gradient-to-*`) or a linked CSS framework. A `<script>` CDN for GSAP/anime.js is fine |
3187
3216
  | `page-structure` / `bullet-list` | error/warn | `<nav>`/`<header>`/`<footer>`/`<table>`; a `<ul>` with visible bullet markers |
3188
- | `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) |
3217
+ | `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). **A custom family the composition DECLARES** (`@font-face` or a Google Fonts `@import` naming it) **is not flagged** — the rule targets the silent fallback, not your typography. Each regime family has a reference card at `https://vidfarm.cc/assets/fonts/caption-font-<family>.png`; compare a still against it after styling |
3189
3218
  | `font-size` / `font-weight` | error/warn | `font-size:0` (invisible) is an error; sub-2.6%-of-canvas-width text and weight <600 warn |
3190
3219
  | `caption-safe-zone` | warn | Text outside the 8%–85% band on a **portrait** canvas (landscape/square are exempt) |
3191
3220
  | `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 |
@@ -4562,7 +4591,8 @@ Two ways in, depending on where the format came from:
4562
4591
 
4563
4592
  ```bash
4564
4593
  # (a) From a bundled base — when the format is one you're defining
4565
- vidfarm harness list # short-form | hooks | ugc-testimonial | explainer | product-demo | product-explainer
4594
+ vidfarm harness list # bases: short-form | hooks | ugc-testimonial | explainer | product-demo | product-explainer
4595
+ # + format harnesses: meme-recaption | wall-text-pov-ugc | … (the /experimental shelf, offline)
4566
4596
  vidfarm harness init hooks --out ./work/HARNESS.md
4567
4597
 
4568
4598
  # (b) From the template you're batching — when the format is one you're REPLICATING
package/SKILL.md CHANGED
@@ -95,6 +95,11 @@ vidfarm publish <forkId> # push edits back to th
95
95
  | A free plan / a 402 / cost mode `minimize` on any search or download | `vidfarm browser setup`, then `vidfarm browse videos\|images\|news\|page "<q>"`. Never answer a sourcing ask with "that needs a paid plan." | `references/browser-harness.md` |
96
96
  | "Make a video about what just happened" | two stages: `vidfarm news-search "<topic>" --fresh w` for the STORY, then `video-search` for the VISUALS | `vidfarm.cc/experimental/google-news-to-video.md` |
97
97
  | "Recaption this meme" / "make a meme for our product" / "a reaction video" | cast a MemeScreens raw for the caption's **verb** (`vidfarm public-raws --category greenscreen`), key it onto a background world, one static caption. $0 | `vidfarm.cc/experimental/meme-recaption.md` |
98
+ | "A UGC ad for my app" / "someone reacting, then the app" / a client's app demo you have to make watchable | three streams cut against each other: `ugc-reaction` raws (ONE actor, via `actor_<uuid>`), a Display Greenscreen device carrying their real demo on its screen, and the demo. Ships a voiceover-only cut to publish + a voiceover+music cut to review | `vidfarm.cc/experimental/ugc-reaction-greenscreen.md` |
99
+ | "A tips slideshow / carousel" / "5 tips for X" / a listicle post | N still slides at exactly 3.0s, die-cut cutouts on a page or a photo, a literal "Tips for…" cover, ONE slide shilled from the middle. **The slides are the deliverable; the MP4 is the preview.** $0–$0.15 | `vidfarm.cc/experimental/sticker-slideshow-tips.md` |
100
+ | "A confession / truth bomb over a POV or ambient scene" / "just text on a video" / a text-story post | ONE unbroken take + ONE static block of unplated type. Nothing animates, nothing is cut. `duration = words / 8` (min 8s) so one play reaches the HALFWAY mark and they loop to finish. Cast the scene DARK so the type needs no plate. $0 | `vidfarm.cc/experimental/wall-text-pov-ugc.md` |
101
+ | "An animated explainer / a little story that animates" / "like those Vox map animations" / "cutout paper animation" | ONE full-bleed parchment stage, no cuts, a cast of die-cut paper stickers moved by ONE paused GSAP timeline. Narrated, subtitles stay small and kinetic in colour only, the offer named once as a wordmark on the LAST beat. Buy SHEETS, not stickers. Desktop-only (the web editor strips scripts). ~$0.25 hybrid, $0 minimize | `vidfarm.cc/experimental/animated-sticker-story.md` |
102
+ | "Make N videos for N customers / clients" / a batch that must not read as N runs of one template | differentiation is an INPUT, not a hope: assign each variant its own frame before you build any of them, then review frame by frame. $0 | `vidfarm.cc/experimental/unique-product-explainers.md` |
98
103
  | "Download this video from `<url>`" | `vidfarm download-video <url>` (paid). Free plan gets a 402 — `vidfarm browse page "<url>"` and save it from their Chrome yourself, else have them download it, then `vidfarm put-file`. Never answer "I can't." | `references/browser-harness.md` |
99
104
  | "Turn this thread / subreddit / profile into a video" | `vidfarm recycle <source>` — returns the raw decomposition, unranked; you pick the hook (paid) | `references/assets-and-sourcing.md` |
100
105
  | "Create an avatar / spokesperson / talking head" | `vidfarm avatar "<who>" --say "<line>"` — a greenscreen talking-head video, keyed in the same job | `references/primitives.md` |
@@ -192,6 +197,7 @@ Otherwise fetch `https://vidfarm.cc/skill-pack/vidfarm/files/<path>`. Load one f
192
197
  | `references/rest-api.md` | direct HTTP integration only |
193
198
  | `recipes/*.md` | `find-and-fork-template` · `retheme-template` · `local-edit-render-approve` · `onboard-a-new-director` · `bulk-scripting-with-a-harness` · `cutout-graphics-for-explainers` |
194
199
  | `harnesses/README.md` | anything harness-shaped — start here. Bases: `short-form` · `hooks` · `ugc-testimonial` · `explainer` · `product-demo` · `product-explainer` |
200
+ | `vidfarm harness list` | both shelves: the bases above, **plus every format harness from `vidfarm.cc/experimental`** — `meme-recaption` · `wall-text-pov-ugc` · `ugc-reaction-greenscreen` · `sticker-slideshow-tips` · `animated-sticker-story` · `google-news-to-video` · `unique-product-explainers`. They ship in the package, so `vidfarm harness show <name>` reads one in full with no fetch, and `vidfarm qa ./work --harness <name>` grades against it. The name is the URL slug |
195
201
 
196
202
  Also served at `vidfarm.cc`: `/experiments.md` (ad testing), `/marketplace.md` (the marketplace manual — routes you to one of the two side harnesses below), `/marketplace-buyer.md` (**buyer side**: commission videos from the crowd), `/agentic-clipper.md` (**worker side**: "Agentic Clipper" mode — one orchestrator on a long-horizon earning mission, one subagent per task), `/update.md` (upgrade runbook), `/experimental` (format harnesses under live testing — the index a clipper routes tasks against), `/skill/vidfarm-platform` (architecture), `/skill/hyperframes` (composition-authoring craft — route broad "make me a video" asks here first).
197
203
 
package/clipper.md CHANGED
@@ -279,8 +279,10 @@ What matters for a clipper loop specifically:
279
279
  built like a meme recaption gets rejected, and so does the reverse. Read the live index at
280
280
  <https://vidfarm.cc/experimental> and pick — a product/feature introduction routes to
281
281
  `unique-product-explainers.md`, a recaption or reaction routes to `meme-recaption.md`, a timely
282
- event routes to `google-news-to-video.md`. Nothing fits? Fall back to a CLI base
283
- (`vidfarm harness list`) and if no harness fits at all, **freestyle it against the best practices
282
+ event routes to `google-news-to-video.md`. Each of those also ships inside the CLI under the same
283
+ name, so `vidfarm harness show meme-recaption` reads the whole contract with no fetch, and
284
+ `vidfarm qa ./work --harness meme-recaption` grades the build against it. Nothing fits? Fall back to
285
+ a CLI base (`vidfarm harness list`) — and if no harness fits at all, **freestyle it against the best practices
284
286
  in <https://vidfarm.cc/skill.md>.** That is a normal outcome, not a failure: harnesses reproduce a
285
287
  *known* format, and a task outside every known format is still one you can deliver well. Never force
286
288
  a task into the wrong harness, and never decline one just because no harness matched.