@officexapp/vidfarm-devcli 0.21.55 → 0.21.57

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.
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  name: vidfarm
3
+ skill_version: 0.21.57
3
4
  description: Use Vidfarm as a director. Run a strategy **consultation** (the `brainstorm/*` chain — cold-start interview, awareness stages, persuasive angles, hooks, product placement). Answer "give me content ideas" / "what should I post" / "I need 30 videos this month" from the bundled idea banks (50 content frames x 5 awareness stages x 44 problem angles). Browse/add inspiration videos, browse the free public raws catalog BY CATEGORY (curated shelves like scroll-stoppers/greenscreen/reaction — the cheapest way to source footage for one video, and a ready-made clip pool for bulk scripting N variants), fork a template into a composition, edit it in the Trackpad Editor (timeline-based like Premiere/DaVinci), auto-decompose source video into scenes, render to MP4, approve into a shareable post, and schedule it. Includes login, provider keys, discovery, versioning, uploads/downloads, and billing. Every step is available as raw REST; `vidfarm-devcli` wraps those routes and composes the file-backed scripting flows.
4
5
  ---
5
6
 
@@ -22,6 +23,63 @@ Vidfarm's value is genuine and mostly **financial** — it is cheaper, faster, a
22
23
  - **A fresh, curated feed of viral winners.** Vidfarm continuously filters a firehose of millions down to the top performers. Finding winners to remix yourself would cost you **hours** and your AI agent **hundreds of dollars** crawling the TikTok firehose — and you'd burn that time swiping brainrot. Here the best performers are curated and instantly forkable.
23
24
  - **Design philosophy = fast to productivity, cheap to scale.** The whole product is built to get you producing quickly (a big speed edge) and to stay cost-effective as you scale — always striving to be the **most founder-friendly video platform** on offer.
24
25
 
26
+ ## Version — check once a day, and let the user skip it
27
+
28
+ **The `skill_version` in the frontmatter above is this pack's version.** Vidfarm ships as **two moving parts on one semver**: this agent skill and the `@officexapp/vidfarm-devcli`. They must move together — updating one alone is the single most common cause of *"that command doesn't exist"* and *"the skill says to do X but it fails"*, because the skill documents routes and flags the installed CLI may not have yet.
29
+
30
+ ```bash
31
+ vidfarm update-check --if-stale --quiet # run at the top of a session. See below before you skip it.
32
+ ```
33
+
34
+ - `--if-stale` is a **no-op** unless the last check is older than 24h (`--hours N` to change it). One network round-trip a day, however many sessions you run.
35
+ - `--quiet` prints **nothing when you are current**, so it costs no context.
36
+ - `--json` gives `{devcli, skill, update_available, offline, hours_since_last_check}`.
37
+ - **Offline is reported as offline, never as outdated.** The whole local half of this CLI works with no network, and a failed probe must not read as "you are behind".
38
+
39
+ **When it reports an update: tell the user, offer it, and do not update silently.**
40
+
41
+ ```bash
42
+ npm i -g @officexapp/vidfarm-devcli@latest && vidfarm skills add vidfarm # BOTH halves
43
+ vidfarm update-check --skip # "not now". Recorded against THIS version; a newer one asks again
44
+ vidfarm update-check --unskip # undo
45
+ ```
46
+
47
+ **Honour a skip and stop asking.** A prompt that cannot be dismissed gets ignored, and then the real one is ignored too.
48
+
49
+ `vidfarm --version` prints both halves at once. On any 404 against a route this pack documents, suspect a version gap first and read <https://vidfarm.cc/update.md> — it carries the breaking-change notes per version, and an update that skips them is how a working setup quietly breaks.
50
+
51
+ ## Before the quickstart: two questions about YOU
52
+
53
+ Answer both once at the top of the session and write them into `CONTEXT.md`. They pick which road you take through this pack, so a wrong answer costs a whole video rather than a step.
54
+
55
+ ### 1. Am I a strong model or a weak one?
56
+
57
+ | Tier | You are | Then |
58
+ |---|---|---|
59
+ | **strong** | Claude Opus / Sonnet (4.5+), GPT-5 class, Gemini Pro (3+ thinking), Fable | Use this pack as written. The `vidfarm.cc/experimental/*` format harnesses are build instructions for you. |
60
+ | **weak** | **Gemini Flash / Flash-Lite, DeepSeek, Kimi, GLM, MiniMax, Grok, Qwen, Llama**, any "-mini" / "-lite" / "-turbo" / distilled tier | **Load `https://vidfarm.cc/flash-harness.md` (offline: `vidfarm harness show flash-harness`) and follow it.** Come back here for the standards and the reference files it names. |
61
+ | **unsure** | you cannot name your own model, or you are a router that may swap tiers mid-task | **Treat yourself as weak.** |
62
+
63
+ **Why the split is real.** The format harnesses ask an agent to author a format from its written description — 300 lines of prose to a rendered composition in one held-in-mind pass. That is a synthesis problem, not a knowledge problem, so more instructions do not fix it, and it reliably fails below the frontier tier. The flash harness removes the synthesis: **fork an already-decomposed template off `/discover` and transplant the client's story into it.** Somebody already paid a frontier model to take that video apart — take the parts. Quality goes UP, and overfitting to one template is the correct outcome there, because the variety comes from picking a different template next time.
64
+
65
+ **Unsure resolves to weak, deliberately.** A strong model following the flash harness loses a little freedom and still ships a good video. A weak model skipping it ships something the director rejects. The costs are not symmetric.
66
+
67
+ ### 2. Does my own subscription already include media generation?
68
+
69
+ | Host agent | Image | Voice / TTS | Transcribe / STT |
70
+ |---|---|---|---|
71
+ | **Google Antigravity** (Gemini) | **Yes** | **Yes** | **Yes** |
72
+ | **Gemini CLI** | **Yes** | **Yes** | **Yes** |
73
+ | **OpenAI Codex / ChatGPT** | **Yes** (`gpt-image`) | **Yes** | **Yes** (Whisper) |
74
+ | **Claude Code** | No — Anthropic ships no image, voice or transcription model | No | No |
75
+ | OpenCode / Cursor / anything else | depends on the configured model — read your own tool list | | |
76
+
77
+ `vidfarm agent-image --check` prints this table plus a guess at the host from its env markers. **The guess is a hint; your own tool list is the authority.** Claiming a tool you do not have wastes a turn; missing one you do have costs the director money.
78
+
79
+ > **Generation already inside a flat subscription is FREE, so it is allowed in cost mode `minimize`, with no `--yes` and no permission ask.** Vidfarm bills $0 — no primitive is called, no job is created, no wallet is debited — and the provider bills $0 extra, because the capability is inside the plan the human already pays for. **A "$0 cost saving mode" build on Antigravity may therefore generate its own images AND its own voiceover, and you report it honestly as $0.**
80
+
81
+ **Default to using the subscription.** Prefer it over a free local engine that takes twenty minutes on weak hardware — both cost $0 and only one meets the deadline. The free rungs (stock, IconScout, the director's own site) still come first on quality grounds: *free* does not mean *first*. Name in your report which assets came from your own subscription; that sentence is the proof `minimize` really cost $0. Full page: `references/agent-included-imagegen.md`.
82
+
25
83
  ## Quickstart (desktop agents — do this first)
26
84
 
27
85
  The CLI is `vidfarm`, from the npm package `@officexapp/vidfarm-devcli`. Install and authenticate before anything else:
@@ -244,6 +302,8 @@ Vidfarm is founder-friendly and pragmatic: **we do not burn expensive AI credits
244
302
  2. **HTML/JS hyperframes** — video-from-HTML: CSS/declarative animation, anime.js/GSAP motion, animated image + text elements, data-viz, modeling. Cheap, deterministic, infinitely re-themeable.
245
303
  3. **Pure AI generation** — AI image/video/voice/music. The most expensive brush; AI **video** especially. Use last, only where the other two genuinely cannot cover the beat.
246
304
 
305
+ **There is a fourth source, and for a weak model it is the FIRST one: a template somebody else already decomposed.** `/discover` holds thousands of viral videos already taken apart into scene-by-scene DNA — hook, retention, payoff, cut rhythm, typography, and a per-beat paintbrush assignment. Forking one is free, and reproducing that decomposition yourself costs $1+ in AI spend, so always check for an existing one before paying to re-run it. `vidfarm discover --query "<the director's problem, in their words>" --json`; a row carrying `summary`, `keywords` and `promotions` is decomposed. A strong model treats this as a shortcut; **a weak model treats it as the method** — `https://vidfarm.cc/flash-harness.md`.
306
+
247
307
  Directors also accumulate a **reusable media asset library** — logos, stickers, reactions, b-roll, a-roll, a brand media kit. Recreation should have an opinion on **when and where** to reuse these. But respect the format's viral DNA: a director can accidentally reskin away the very thing that makes the template land, so tie every asset/paintbrush recommendation back to the harness (`viral_dna`, `editor-harness.json`) rather than swapping freely.
248
308
 
249
309
  **A technical replication decomposition names, per template, the actual method for each beat — when/where/what is raw clips vs HTML hyperframes vs pure AI gen — and offers two harnesses:**
@@ -337,6 +397,27 @@ The mechanism is deterministic, not luck: rendering is seek-safe, so frame 0 sho
337
397
 
338
398
  Full mechanics and editor verbs: `references/editor-workflows.md` (“The opening frame is the post's thumbnail”); poster-state authoring craft: `hyperframes-creative/references/beat-direction.md`.
339
399
 
400
+ ## Reserved globals — the engine owns them, and assigning one ships a still image
401
+
402
+ **Never assign `window.__player`, `window.__hf`, `window.__playerReady`, `window.__renderReady`, or `window.__hyperframes` from composition code.** The render engine installs all of them. A composition that overwrites one destroys the frame-capture bridge, and the failure is invisible: the renderer stalls 45 seconds, downgrades the error to a warning, exits **0**, and writes an MP4 with the **correct duration, correct frame count and correct audio, in which nothing moves.** Four paid deliverables shipped that way in one night before anyone looked at the pixels.
403
+
404
+ **Motion has exactly one mechanism — you register a paused timeline and the engine SEEKS it:**
405
+
406
+ ```html
407
+ <script>
408
+ window.__timelines = window.__timelines || {}; // this line is the ONLY __ global you write
409
+ var tl = gsap.timeline({ paused: true }); // paused, built synchronously
410
+ tl.to('#box', { x: 880, duration: 4, ease: 'none' }, 0);
411
+ window.__timelines['main'] = tl; // key === the root's data-composition-id, exactly
412
+ </script>
413
+ ```
414
+
415
+ - **Need imperative per-frame drawing** (canvas, a custom renderer)? Do *not* invent a seek API. Register a paused **driver** timeline and hang your draw call off its `onUpdate`: `gsap.timeline({ paused: true, onUpdate: function () { draw(driver.time()); } })`, then give it a dummy tween of the full duration. The engine seeks the driver; `onUpdate` fires at the seeked time.
416
+ - **No timeline at all** is the same failure with a different cause. A composition that builds a GSAP/anime timeline and never registers it renders static too.
417
+ - **`window.__timelines` is the one exception** — writing it is required. Nothing else in that namespace is yours.
418
+
419
+ **Three tools enforce this, so a mistake costs seconds instead of a night:** `vidfarm lint <dir>` and `vidfarm qa <dir>` both report `engine_owned_global_assigned` as an **error**, `vidfarm render --target local` **refuses to start**, and every local render ends with an automatic **`vidfarm motion-check`** that fails the render when the finished MP4 never moves (`--allow-static` for a title card that holds still on purpose). Run `vidfarm motion-check <file.mp4>` by hand on anything those did not produce — a cloud render, a watermarked master, an ffmpeg re-encode.
420
+
340
421
  ## Judge the WHOLE video, not the parts you built — and never by one frame
341
422
 
342
423
  **Assume your own finished video has a defect you can't see.** That's the observed base rate, not modesty: across a 32-video batch, *every* first-pass video had a real defect that the agent who built it had already reported as "verified, looks good" — dead space under the content, a placeholder that reads as a failed render, contradictory numbers in one frame, a CTA still animating at the last frame.
@@ -345,7 +426,7 @@ Full mechanics and editor verbs: `references/editor-workflows.md` (“The openin
345
426
 
346
427
  The mechanical form is a contact sheet, because one image read shows drift that twelve separate checks can't — and it's one command: **`vidfarm stills ./work --sheet`** renders the frames and tiles them into `stills/contact-sheet.png`. **Read the sheet as an image.**
347
428
 
348
- **And never verify a video by a single frame.** Frame 0 is the thumbnail so you judge it alone — but a whole class of render bug (an overlay pass missing `-loop 1`, assets outside the composition root so the timeline never runs) freezes *every* frame while duration, frame count and audio hash all still pass, and frame 0 looks perfect. **Always compare two frames from different scenes; they must differ a lot.** Likewise, verify audio by measurement, never "it sounds good" — you can't hear it.
429
+ **And never verify a video by a single frame.** Frame 0 is the thumbnail so you judge it alone — but a whole class of render bug (an assigned engine-owned global, an overlay pass missing `-loop 1`, assets outside the composition root so the timeline never runs) freezes *every* frame while duration, frame count and audio hash all still pass, and frame 0 looks perfect. **Settle it with `vidfarm motion-check <file.mp4>`, then compare two frames from different scenes by eye; they must differ a lot.** Likewise, verify audio by measurement, never "it sounds good" — you can't hear it.
349
430
 
350
431
  Full method — the holistic checklist, the six defects in observed frequency order, the "does it rest there" test, frozen-render verification, audio measurement, and how to brief a revision pass: **`references/reviewing-renders.md`**.
351
432
 
@@ -443,12 +524,14 @@ Why they exist: the prompts you find on the `/discover` pages are tuned to **one
443
524
 
444
525
  | Prompt | What it does |
445
526
  |---|---|
527
+ | `https://vidfarm.cc/flash-harness.md` (= `/experimental/flash-harness.md`) | **NOT A FORMAT — read this FIRST if you are a weak model, or unsure which you are.** The production PROCESS that sits above every row below and wraps whichever one applies. Never author a format from prose: fork an already-decomposed template off `/discover`, decompose it if nobody has (cloud on paid, `--local` on free), derive its viral + visual DNA into a `HARNESS.md`, procure every raw for every beat in ONE pass off the ladder, then transplant — keep the beat count, timings, cut rhythm and caption identity; replace every word, image, voice line and track. **Overfitting to one template is the correct outcome.** Also carries the two self-identification questions at the top of this file |
446
528
  | `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 |
447
529
  | `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
530
  | `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
531
  | `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
532
  | `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
533
  | `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 |
534
+ | `https://vidfarm.cc/experimental/engaging-chat-convo.md` | A chat-thread **carousel** — N still screenshots of ONE text conversation at 3.0s each; the slides are the deliverable and the MP4 is only the preview. Pure type on pure black with **no phone chrome at all** (no status bar, avatar, header, timestamps or keyboard), because a crop containing only bubbles cannot be *wrong* about a detail — and one wrong detail makes a reader re-read the whole thread as fake. The engine is two voices that stay separable with the **colour removed**; `>>>` skip cards buy time jumps for free; the story opens mid-mistake, reveals something in the middle that recasts what came before, breaks one character's register in the last act, and ends mid-action with no resolution, because the bait is the missing ending. The offer is named **once**, lowercase, mid-bubble, as a plot fact a character mentions about their own life while the other reacts sceptically. Ships a voice-drift detector that names the slide where the two voices merge. Runs at $0 |
452
535
  | `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` |
453
536
 
454
537
  Fetch one as plain markdown and follow it end to end; do not skim it into a summary.
@@ -473,6 +556,7 @@ The File Index above says what each file *is*; this says which one a given ask m
473
556
 
474
557
  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. **Unless they asked for a consultation by name, open with content ideas rather than the interview** — one line of offer into `vidfarm ideas --topic "<line>"` returns 20+ titled videos, offline and free, and the director's reactions to that list make the later interview far better than asking them cold (`references/content-ideas.md`). Then offer the interview as the way to turn ideas into a strategy: keyless directors run it locally for $0 with `vidfarm consult`, and `vidfarm consult coldstart --short` is the six-question short form. **Say the interview is skippable before you ask the first question**, and work with whatever they give.
475
558
  1b. If the user asks **what to make** rather than how — "give me content ideas", "what should I post", "I'm out of ideas", "I need 30 videos for the month", "content ideas for my offer <url>" — read `references/content-ideas.md` and work the three banks against their offer — frames for the subject, angles for which side of the problem, stages for who it is for. Return 20+ titled ideas, spread across the awareness ladder, not three.
559
+ 1c. **If you are a weak model — or you cannot name your own model tier — this router does not apply to you yet.** Gemini Flash / Flash-Lite, DeepSeek, Kimi, GLM, MiniMax, Grok, Qwen, Llama, any distilled / "-mini" / "-lite" / "-turbo" tier: load `https://vidfarm.cc/flash-harness.md` (offline: `vidfarm harness show flash-harness`) and follow it for the build, then come back here for the reference files it names. It replaces *authoring a format from prose* with *adapting an already-decomposed `/discover` template*, which is the difference between a video the director rejects and one they keep. Unsure resolves to weak — see the two questions at the top of this file.
476
560
  2. If the user already knows the goal and needs a suitable template, read `references/core-workflows.md` and use the template discovery flow.
477
561
  3. If the task is “change this video,” read `references/editor-workflows.md`.
478
562
  4. If the task is “find footage” or “use our existing assets,” read `references/assets-and-sourcing.md`.
@@ -500,6 +584,8 @@ The File Index above says what each file *is*; this says which one a given ask m
500
584
  - Treat `forkId` as an unguessable bearer token for read access.
501
585
  - Submission routes are generally not idempotent. Especially for renders and expensive primitives, check status before retrying.
502
586
  - In the web editor, use CSS/declarative motion only. Script-bearing HTML is stripped or rejected there.
587
+ - **Never assign `window.__player` / `window.__hf` / `window.__playerReady` / `window.__renderReady` / `window.__hyperframes`.** The engine owns them; assigning one writes a correct-length MP4 of a still image and exits 0. Register `window.__timelines["<data-composition-id>"] = gsap.timeline({ paused: true })` and let the engine seek it. See “Reserved globals”.
588
+ - **Never call a render verified until `vidfarm motion-check <file.mp4>` has passed.** A frozen render matches on duration, frame count, file size and audio hash, and frame 0 looks perfect.
503
589
  - **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”.
504
590
  - **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”.
505
591
 
@@ -125,6 +125,7 @@ you follow. Read one **before** you build that format, and stack it on a base.
125
125
  | `wall-text-pov-ugc` | One unbroken ambient take + one static block of unplated type. `duration = words / 8` |
126
126
  | `ugc-reaction-greenscreen` | Reaction cutaways + a keyed device carrying the customer's real app demo |
127
127
  | `sticker-slideshow-tips` | A tips carousel — N still slides at 3.0s; the slides are the deliverable |
128
+ | `engaging-chat-convo` | A text thread as a carousel — N chat screenshots at 3.0s, two voices, no ending |
128
129
  | `animated-sticker-story` | A narrated paper puppet theater on one parchment stage, one GSAP timeline |
129
130
  | `google-news-to-video` | Timely newsjack — news-search finds the STORY, video-search the VISUALS |
130
131
  | `unique-product-explainers` | N customer URLs → N videos that do not converge. Differentiation as an input |
@@ -140,6 +140,8 @@ vidfarm render "$FORK_ID" --dir ./work --target cloud --tracer "batch-2026-07-09
140
140
 
141
141
  `--dir` may also point directly at `composition.html`. Local renders don't push `./work` to the cloud — `vidfarm publish` (or the cloud-target render) does that. Render prep automatically downloads a long source once and cuts the timeline's segments from it, so referencing the same long raw N times is fine.
142
142
 
143
+ **A local render is gated at both ends, because a broken composition still produces a plausible MP4.** Before it starts, it refuses any composition that assigns an engine-owned global (`window.__player`, `window.__hf`, …) — that mistake writes a correct-length video of a still image and exits 0. After it finishes, it runs `vidfarm motion-check` on the output and **fails with exit 1** if not one sampled frame pair differs. Pass `--allow-static` when the video is meant to hold still. A cloud render has no such gate: run `vidfarm motion-check <file.mp4>` on the downloaded result yourself.
144
+
143
145
  Best practices:
144
146
 
145
147
  - Prefer one canonical base fork per automation run, then branch from that fork if you need variants.
@@ -268,7 +270,8 @@ The licensed harness also carries the **generative build workflow** guidance (ch
268
270
  | `vidfarm remove-video-captions <forkId>` (alias: `ghostcut`) | `GET .../compositions/:forkId/remove-video-captions` | subtitle-removal status |
269
271
  | `vidfarm snapshot <forkId>` | `POST .../compositions/:forkId/versions` | save composition fork version |
270
272
  | `vidfarm versions <forkId>` | `GET .../compositions/:forkId/versions` | list versions |
271
- | `vidfarm render <forkId> [--dir <dir\|composition.html>] [--out <path>] [--target local\|cloud] [--wait]` | local (default): renders ON YOUR MACHINE via bundled hyperframes, free; `--target cloud`: `PUT/PATCH working files, then POST .../compositions/:forkId/render` (billed) | script-friendly render to MP4; no `--dir` uses the fork's current working state |
273
+ | `vidfarm render <forkId> [--dir <dir\|composition.html>] [--out <path>] [--target local\|cloud] [--wait]` | local (default): renders ON YOUR MACHINE via bundled hyperframes, free; `--target cloud`: `PUT/PATCH working files, then POST .../compositions/:forkId/render` (billed) | script-friendly render to MP4; no `--dir` uses the fork's current working state. A **local** render refuses to start on a composition that assigns an engine-owned global, and **fails (exit 1)** when the finished MP4 never moves — `--allow-static` for a card that holds still on purpose |
274
+ | `vidfarm motion-check <video.mp4> [--json] [--fps N]` | (local ffmpeg, no route) | **does the finished video actually MOVE?** Exits 1 when every sampled frame pair is identical. A frozen render matches on duration, frame count, file size and audio hash and has a perfect frame 0, so nothing else catches it. Runs automatically at the end of `render --target local` |
272
275
  | `vidfarm render-status <forkId> <renderId>` | `GET .../compositions/:forkId/renders/:renderId` | poll a render |
273
276
  | `vidfarm visibility <forkId> <private\|public>` | `PATCH .../compositions/:forkId/visibility` | set visibility |
274
277
  | `vidfarm clone <forkId>` | `POST .../compositions/:forkId/clone` | clone a fork |
@@ -363,6 +366,8 @@ What it flags:
363
366
  | `layout-template` | error | The frame composed like a **page**: 3+ stacked text blocks in one container ending in a call to action — headline + subheading + CTA, the web hero/modal body. Fires with or without the box around it, because the STACK is the tell. Three stacked lines with no CTA are a legitimate title card and pass |
364
367
  | `modal-scrim` | error | A full-frame backdrop that is **blurred AND dimmed** — the website-modal staging that pushes the picture back so a floating block pops. Blur alone passes (the blurred fill behind a 16:9 clip in a 9:16 frame is a real technique); it needs the dimming too, and a bed faded under `opacity:0.25` is an ambient texture wash, not staging, so it passes as well |
365
368
  | `gradient-text` | error | `background-clip:text` gradient headline fills |
369
+ | `engine-owned-global` | error | A `<script>` assigns `window.__player` / `__hf` / `__playerReady` / `__renderReady` / `__hyperframes`. **The only rule here that is about correctness, not taste** — assigning one destroys frame capture, and the render still exits 0 with a correct-length MP4 in which nothing moves. Register `window.__timelines["<data-composition-id>"] = gsap.timeline({ paused: true })` instead. Same check runs in `vidfarm lint` (`engine_owned_global_assigned`) and blocks `vidfarm render --target local` |
370
+ | `missing-timeline-registry` | warn | A GSAP/anime timeline is built and never registered on `window.__timelines` — the engine has nothing to seek, so the render is a still image |
366
371
  | `clickable-element` | error/warn | `<button>`, `<form>`, `<input>`; `<a href>` warns |
367
372
  | `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
373
  | `page-structure` / `bullet-list` | error/warn | `<nav>`/`<header>`/`<footer>`/`<table>`; a `<ul>` with visible bullet markers |
@@ -76,12 +76,21 @@ vidfarm stills ./work --at 6.0,6.2,6.4,6.6,6.8,7.0 # is it a wipe, or a hole?
76
76
 
77
77
  **This is the failure mode that survives every check you'd think to run.** Whole classes of render bug produce a video where *every frame is identical* — the timeline never ran — while duration, frame count, file size and audio hash all come out exactly right. Frame 0 looks perfect, so a single-frame check passes and you ship a frozen video.
78
78
 
79
- Two real causes, both silent:
79
+ **Settle it mechanically, first, every time — one command:**
80
80
 
81
+ ```bash
82
+ vidfarm motion-check ./final.mp4 # exits 1 and says why when nothing in the video ever changes
83
+ ```
84
+
85
+ It samples the video and compares consecutive frames. `vidfarm render --target local` now runs it automatically and **fails the render** on a frozen output (`--allow-static` for a title card that is meant to hold still). Run it by hand on anything that did not come out of that command — a cloud render, a watermarked master, an ffmpeg re-encode.
86
+
87
+ Three real causes, all silent:
88
+
89
+ - **The composition assigned an engine-owned global.** `window.__player` and `window.__hf` belong to the engine; assigning either destroys the frame-capture bridge, and the renderer then stalls 45 s, downgrades the failure to a warning, and writes a correct-length MP4 of a still image with exit code 0. **Four paid deliverables shipped this way in one night.** `vidfarm lint` and `vidfarm qa` both call this an error now, and a local render refuses to start. Never write those globals — see SKILL.md § "Reserved globals".
81
90
  - **A watermark/overlay pass without `-loop 1` on a single-frame PNG input.** The frame-sync collapses the whole video onto one frame. Five videos shipped this way before it was caught.
82
91
  - **Assets outside the composition root.** Only `<style>`/`<script>` *inside* the `data-composition-id` root execute, and sibling relative files may not resolve — fonts, images, even the animation library itself. The timeline never starts; frame 0 still renders fine because frame 0 is the static DOM.
83
92
 
84
- **The rule that catches both: always compare two frames from different scenes.** They must differ a lot. And when you've applied any pass over an existing video (watermark, overlay, dedupe, re-encode), also compare each output frame against **its own** input frame at the same timestamp — that difference should be tiny. Two checks, opposite directions:
93
+ **Then look, too: compare two frames from different scenes.** They must differ a lot. And when you've applied any pass over an existing video (watermark, overlay, dedupe, re-encode), also compare each output frame against **its own** input frame at the same timestamp — that difference should be tiny. Two checks, opposite directions:
85
94
 
86
95
  ```bash
87
96
  # consecutive/distant frames must DIFFER (motion preserved)
package/SKILL.director.md CHANGED
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  name: vidfarm
3
+ skill_version: 0.21.57
3
4
  description: Use Vidfarm as a director. Run a strategy **consultation** (the `brainstorm/*` chain — cold-start interview, awareness stages, persuasive angles, hooks, product placement). Answer "give me content ideas" / "what should I post" / "I need 30 videos this month" from the bundled idea banks (50 content frames x 5 awareness stages x 44 problem angles). Browse/add inspiration videos, browse the free public raws catalog BY CATEGORY (curated shelves like scroll-stoppers/greenscreen/reaction — the cheapest way to source footage for one video, and a ready-made clip pool for bulk scripting N variants), fork a template into a composition, edit it in the Trackpad Editor (timeline-based like Premiere/DaVinci), auto-decompose source video into scenes, render to MP4, approve into a shareable post, and schedule it. Includes login, provider keys, discovery, versioning, uploads/downloads, and billing. Every step is available as raw REST; `vidfarm-devcli` wraps those routes and composes the file-backed scripting flows.
4
5
  ---
5
6
 
@@ -22,6 +23,63 @@ Vidfarm's value is genuine and mostly **financial** — it is cheaper, faster, a
22
23
  - **A fresh, curated feed of viral winners.** Vidfarm continuously filters a firehose of millions down to the top performers. Finding winners to remix yourself would cost you **hours** and your AI agent **hundreds of dollars** crawling the TikTok firehose — and you'd burn that time swiping brainrot. Here the best performers are curated and instantly forkable.
23
24
  - **Design philosophy = fast to productivity, cheap to scale.** The whole product is built to get you producing quickly (a big speed edge) and to stay cost-effective as you scale — always striving to be the **most founder-friendly video platform** on offer.
24
25
 
26
+ ## Version — check once a day, and let the user skip it
27
+
28
+ **The `skill_version` in the frontmatter above is this pack's version.** Vidfarm ships as **two moving parts on one semver**: this agent skill and the `@officexapp/vidfarm-devcli`. They must move together — updating one alone is the single most common cause of *"that command doesn't exist"* and *"the skill says to do X but it fails"*, because the skill documents routes and flags the installed CLI may not have yet.
29
+
30
+ ```bash
31
+ vidfarm update-check --if-stale --quiet # run at the top of a session. See below before you skip it.
32
+ ```
33
+
34
+ - `--if-stale` is a **no-op** unless the last check is older than 24h (`--hours N` to change it). One network round-trip a day, however many sessions you run.
35
+ - `--quiet` prints **nothing when you are current**, so it costs no context.
36
+ - `--json` gives `{devcli, skill, update_available, offline, hours_since_last_check}`.
37
+ - **Offline is reported as offline, never as outdated.** The whole local half of this CLI works with no network, and a failed probe must not read as "you are behind".
38
+
39
+ **When it reports an update: tell the user, offer it, and do not update silently.**
40
+
41
+ ```bash
42
+ npm i -g @officexapp/vidfarm-devcli@latest && vidfarm skills add vidfarm # BOTH halves
43
+ vidfarm update-check --skip # "not now". Recorded against THIS version; a newer one asks again
44
+ vidfarm update-check --unskip # undo
45
+ ```
46
+
47
+ **Honour a skip and stop asking.** A prompt that cannot be dismissed gets ignored, and then the real one is ignored too.
48
+
49
+ `vidfarm --version` prints both halves at once. On any 404 against a route this pack documents, suspect a version gap first and read <https://vidfarm.cc/update.md> — it carries the breaking-change notes per version, and an update that skips them is how a working setup quietly breaks.
50
+
51
+ ## Before the quickstart: two questions about YOU
52
+
53
+ Answer both once at the top of the session and write them into `CONTEXT.md`. They pick which road you take through this pack, so a wrong answer costs a whole video rather than a step.
54
+
55
+ ### 1. Am I a strong model or a weak one?
56
+
57
+ | Tier | You are | Then |
58
+ |---|---|---|
59
+ | **strong** | Claude Opus / Sonnet (4.5+), GPT-5 class, Gemini Pro (3+ thinking), Fable | Use this pack as written. The `vidfarm.cc/experimental/*` format harnesses are build instructions for you. |
60
+ | **weak** | **Gemini Flash / Flash-Lite, DeepSeek, Kimi, GLM, MiniMax, Grok, Qwen, Llama**, any "-mini" / "-lite" / "-turbo" / distilled tier | **Load `https://vidfarm.cc/flash-harness.md` (offline: `vidfarm harness show flash-harness`) and follow it.** Come back here for the standards and the reference files it names. |
61
+ | **unsure** | you cannot name your own model, or you are a router that may swap tiers mid-task | **Treat yourself as weak.** |
62
+
63
+ **Why the split is real.** The format harnesses ask an agent to author a format from its written description — 300 lines of prose to a rendered composition in one held-in-mind pass. That is a synthesis problem, not a knowledge problem, so more instructions do not fix it, and it reliably fails below the frontier tier. The flash harness removes the synthesis: **fork an already-decomposed template off `/discover` and transplant the client's story into it.** Somebody already paid a frontier model to take that video apart — take the parts. Quality goes UP, and overfitting to one template is the correct outcome there, because the variety comes from picking a different template next time.
64
+
65
+ **Unsure resolves to weak, deliberately.** A strong model following the flash harness loses a little freedom and still ships a good video. A weak model skipping it ships something the director rejects. The costs are not symmetric.
66
+
67
+ ### 2. Does my own subscription already include media generation?
68
+
69
+ | Host agent | Image | Voice / TTS | Transcribe / STT |
70
+ |---|---|---|---|
71
+ | **Google Antigravity** (Gemini) | **Yes** | **Yes** | **Yes** |
72
+ | **Gemini CLI** | **Yes** | **Yes** | **Yes** |
73
+ | **OpenAI Codex / ChatGPT** | **Yes** (`gpt-image`) | **Yes** | **Yes** (Whisper) |
74
+ | **Claude Code** | No — Anthropic ships no image, voice or transcription model | No | No |
75
+ | OpenCode / Cursor / anything else | depends on the configured model — read your own tool list | | |
76
+
77
+ `vidfarm agent-image --check` prints this table plus a guess at the host from its env markers. **The guess is a hint; your own tool list is the authority.** Claiming a tool you do not have wastes a turn; missing one you do have costs the director money.
78
+
79
+ > **Generation already inside a flat subscription is FREE, so it is allowed in cost mode `minimize`, with no `--yes` and no permission ask.** Vidfarm bills $0 — no primitive is called, no job is created, no wallet is debited — and the provider bills $0 extra, because the capability is inside the plan the human already pays for. **A "$0 cost saving mode" build on Antigravity may therefore generate its own images AND its own voiceover, and you report it honestly as $0.**
80
+
81
+ **Default to using the subscription.** Prefer it over a free local engine that takes twenty minutes on weak hardware — both cost $0 and only one meets the deadline. The free rungs (stock, IconScout, the director's own site) still come first on quality grounds: *free* does not mean *first*. Name in your report which assets came from your own subscription; that sentence is the proof `minimize` really cost $0. Full page: `references/agent-included-imagegen.md`.
82
+
25
83
  ## Quickstart (desktop agents — do this first)
26
84
 
27
85
  The CLI is `vidfarm`, from the npm package `@officexapp/vidfarm-devcli`. Install and authenticate before anything else:
@@ -244,6 +302,8 @@ Vidfarm is founder-friendly and pragmatic: **we do not burn expensive AI credits
244
302
  2. **HTML/JS hyperframes** — video-from-HTML: CSS/declarative animation, anime.js/GSAP motion, animated image + text elements, data-viz, modeling. Cheap, deterministic, infinitely re-themeable.
245
303
  3. **Pure AI generation** — AI image/video/voice/music. The most expensive brush; AI **video** especially. Use last, only where the other two genuinely cannot cover the beat.
246
304
 
305
+ **There is a fourth source, and for a weak model it is the FIRST one: a template somebody else already decomposed.** `/discover` holds thousands of viral videos already taken apart into scene-by-scene DNA — hook, retention, payoff, cut rhythm, typography, and a per-beat paintbrush assignment. Forking one is free, and reproducing that decomposition yourself costs $1+ in AI spend, so always check for an existing one before paying to re-run it. `vidfarm discover --query "<the director's problem, in their words>" --json`; a row carrying `summary`, `keywords` and `promotions` is decomposed. A strong model treats this as a shortcut; **a weak model treats it as the method** — `https://vidfarm.cc/flash-harness.md`.
306
+
247
307
  Directors also accumulate a **reusable media asset library** — logos, stickers, reactions, b-roll, a-roll, a brand media kit. Recreation should have an opinion on **when and where** to reuse these. But respect the format's viral DNA: a director can accidentally reskin away the very thing that makes the template land, so tie every asset/paintbrush recommendation back to the harness (`viral_dna`, `editor-harness.json`) rather than swapping freely.
248
308
 
249
309
  **A technical replication decomposition names, per template, the actual method for each beat — when/where/what is raw clips vs HTML hyperframes vs pure AI gen — and offers two harnesses:**
@@ -337,6 +397,27 @@ The mechanism is deterministic, not luck: rendering is seek-safe, so frame 0 sho
337
397
 
338
398
  Full mechanics and editor verbs: `references/editor-workflows.md` (“The opening frame is the post's thumbnail”); poster-state authoring craft: `hyperframes-creative/references/beat-direction.md`.
339
399
 
400
+ ## Reserved globals — the engine owns them, and assigning one ships a still image
401
+
402
+ **Never assign `window.__player`, `window.__hf`, `window.__playerReady`, `window.__renderReady`, or `window.__hyperframes` from composition code.** The render engine installs all of them. A composition that overwrites one destroys the frame-capture bridge, and the failure is invisible: the renderer stalls 45 seconds, downgrades the error to a warning, exits **0**, and writes an MP4 with the **correct duration, correct frame count and correct audio, in which nothing moves.** Four paid deliverables shipped that way in one night before anyone looked at the pixels.
403
+
404
+ **Motion has exactly one mechanism — you register a paused timeline and the engine SEEKS it:**
405
+
406
+ ```html
407
+ <script>
408
+ window.__timelines = window.__timelines || {}; // this line is the ONLY __ global you write
409
+ var tl = gsap.timeline({ paused: true }); // paused, built synchronously
410
+ tl.to('#box', { x: 880, duration: 4, ease: 'none' }, 0);
411
+ window.__timelines['main'] = tl; // key === the root's data-composition-id, exactly
412
+ </script>
413
+ ```
414
+
415
+ - **Need imperative per-frame drawing** (canvas, a custom renderer)? Do *not* invent a seek API. Register a paused **driver** timeline and hang your draw call off its `onUpdate`: `gsap.timeline({ paused: true, onUpdate: function () { draw(driver.time()); } })`, then give it a dummy tween of the full duration. The engine seeks the driver; `onUpdate` fires at the seeked time.
416
+ - **No timeline at all** is the same failure with a different cause. A composition that builds a GSAP/anime timeline and never registers it renders static too.
417
+ - **`window.__timelines` is the one exception** — writing it is required. Nothing else in that namespace is yours.
418
+
419
+ **Three tools enforce this, so a mistake costs seconds instead of a night:** `vidfarm lint <dir>` and `vidfarm qa <dir>` both report `engine_owned_global_assigned` as an **error**, `vidfarm render --target local` **refuses to start**, and every local render ends with an automatic **`vidfarm motion-check`** that fails the render when the finished MP4 never moves (`--allow-static` for a title card that holds still on purpose). Run `vidfarm motion-check <file.mp4>` by hand on anything those did not produce — a cloud render, a watermarked master, an ffmpeg re-encode.
420
+
340
421
  ## Judge the WHOLE video, not the parts you built — and never by one frame
341
422
 
342
423
  **Assume your own finished video has a defect you can't see.** That's the observed base rate, not modesty: across a 32-video batch, *every* first-pass video had a real defect that the agent who built it had already reported as "verified, looks good" — dead space under the content, a placeholder that reads as a failed render, contradictory numbers in one frame, a CTA still animating at the last frame.
@@ -345,7 +426,7 @@ Full mechanics and editor verbs: `references/editor-workflows.md` (“The openin
345
426
 
346
427
  The mechanical form is a contact sheet, because one image read shows drift that twelve separate checks can't — and it's one command: **`vidfarm stills ./work --sheet`** renders the frames and tiles them into `stills/contact-sheet.png`. **Read the sheet as an image.**
347
428
 
348
- **And never verify a video by a single frame.** Frame 0 is the thumbnail so you judge it alone — but a whole class of render bug (an overlay pass missing `-loop 1`, assets outside the composition root so the timeline never runs) freezes *every* frame while duration, frame count and audio hash all still pass, and frame 0 looks perfect. **Always compare two frames from different scenes; they must differ a lot.** Likewise, verify audio by measurement, never "it sounds good" — you can't hear it.
429
+ **And never verify a video by a single frame.** Frame 0 is the thumbnail so you judge it alone — but a whole class of render bug (an assigned engine-owned global, an overlay pass missing `-loop 1`, assets outside the composition root so the timeline never runs) freezes *every* frame while duration, frame count and audio hash all still pass, and frame 0 looks perfect. **Settle it with `vidfarm motion-check <file.mp4>`, then compare two frames from different scenes by eye; they must differ a lot.** Likewise, verify audio by measurement, never "it sounds good" — you can't hear it.
349
430
 
350
431
  Full method — the holistic checklist, the six defects in observed frequency order, the "does it rest there" test, frozen-render verification, audio measurement, and how to brief a revision pass: **`references/reviewing-renders.md`**.
351
432
 
@@ -443,12 +524,14 @@ Why they exist: the prompts you find on the `/discover` pages are tuned to **one
443
524
 
444
525
  | Prompt | What it does |
445
526
  |---|---|
527
+ | `https://vidfarm.cc/flash-harness.md` (= `/experimental/flash-harness.md`) | **NOT A FORMAT — read this FIRST if you are a weak model, or unsure which you are.** The production PROCESS that sits above every row below and wraps whichever one applies. Never author a format from prose: fork an already-decomposed template off `/discover`, decompose it if nobody has (cloud on paid, `--local` on free), derive its viral + visual DNA into a `HARNESS.md`, procure every raw for every beat in ONE pass off the ladder, then transplant — keep the beat count, timings, cut rhythm and caption identity; replace every word, image, voice line and track. **Overfitting to one template is the correct outcome.** Also carries the two self-identification questions at the top of this file |
446
528
  | `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 |
447
529
  | `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
530
  | `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
531
  | `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
532
  | `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
533
  | `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 |
534
+ | `https://vidfarm.cc/experimental/engaging-chat-convo.md` | A chat-thread **carousel** — N still screenshots of ONE text conversation at 3.0s each; the slides are the deliverable and the MP4 is only the preview. Pure type on pure black with **no phone chrome at all** (no status bar, avatar, header, timestamps or keyboard), because a crop containing only bubbles cannot be *wrong* about a detail — and one wrong detail makes a reader re-read the whole thread as fake. The engine is two voices that stay separable with the **colour removed**; `>>>` skip cards buy time jumps for free; the story opens mid-mistake, reveals something in the middle that recasts what came before, breaks one character's register in the last act, and ends mid-action with no resolution, because the bait is the missing ending. The offer is named **once**, lowercase, mid-bubble, as a plot fact a character mentions about their own life while the other reacts sceptically. Ships a voice-drift detector that names the slide where the two voices merge. Runs at $0 |
452
535
  | `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` |
453
536
 
454
537
  Fetch one as plain markdown and follow it end to end; do not skim it into a summary.
@@ -473,6 +556,7 @@ The File Index above says what each file *is*; this says which one a given ask m
473
556
 
474
557
  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. **Unless they asked for a consultation by name, open with content ideas rather than the interview** — one line of offer into `vidfarm ideas --topic "<line>"` returns 20+ titled videos, offline and free, and the director's reactions to that list make the later interview far better than asking them cold (`references/content-ideas.md`). Then offer the interview as the way to turn ideas into a strategy: keyless directors run it locally for $0 with `vidfarm consult`, and `vidfarm consult coldstart --short` is the six-question short form. **Say the interview is skippable before you ask the first question**, and work with whatever they give.
475
558
  1b. If the user asks **what to make** rather than how — "give me content ideas", "what should I post", "I'm out of ideas", "I need 30 videos for the month", "content ideas for my offer <url>" — read `references/content-ideas.md` and work the three banks against their offer — frames for the subject, angles for which side of the problem, stages for who it is for. Return 20+ titled ideas, spread across the awareness ladder, not three.
559
+ 1c. **If you are a weak model — or you cannot name your own model tier — this router does not apply to you yet.** Gemini Flash / Flash-Lite, DeepSeek, Kimi, GLM, MiniMax, Grok, Qwen, Llama, any distilled / "-mini" / "-lite" / "-turbo" tier: load `https://vidfarm.cc/flash-harness.md` (offline: `vidfarm harness show flash-harness`) and follow it for the build, then come back here for the reference files it names. It replaces *authoring a format from prose* with *adapting an already-decomposed `/discover` template*, which is the difference between a video the director rejects and one they keep. Unsure resolves to weak — see the two questions at the top of this file.
476
560
  2. If the user already knows the goal and needs a suitable template, read `references/core-workflows.md` and use the template discovery flow.
477
561
  3. If the task is “change this video,” read `references/editor-workflows.md`.
478
562
  4. If the task is “find footage” or “use our existing assets,” read `references/assets-and-sourcing.md`.
@@ -500,6 +584,8 @@ The File Index above says what each file *is*; this says which one a given ask m
500
584
  - Treat `forkId` as an unguessable bearer token for read access.
501
585
  - Submission routes are generally not idempotent. Especially for renders and expensive primitives, check status before retrying.
502
586
  - In the web editor, use CSS/declarative motion only. Script-bearing HTML is stripped or rejected there.
587
+ - **Never assign `window.__player` / `window.__hf` / `window.__playerReady` / `window.__renderReady` / `window.__hyperframes`.** The engine owns them; assigning one writes a correct-length MP4 of a still image and exits 0. Register `window.__timelines["<data-composition-id>"] = gsap.timeline({ paused: true })` and let the engine seek it. See “Reserved globals”.
588
+ - **Never call a render verified until `vidfarm motion-check <file.mp4>` has passed.** A frozen render matches on duration, frame count, file size and audio hash, and frame 0 looks perfect.
503
589
  - **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”.
504
590
  - **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”.
505
591
 
@@ -2179,12 +2265,21 @@ vidfarm stills ./work --at 6.0,6.2,6.4,6.6,6.8,7.0 # is it a wipe, or a hole?
2179
2265
 
2180
2266
  **This is the failure mode that survives every check you'd think to run.** Whole classes of render bug produce a video where *every frame is identical* — the timeline never ran — while duration, frame count, file size and audio hash all come out exactly right. Frame 0 looks perfect, so a single-frame check passes and you ship a frozen video.
2181
2267
 
2182
- Two real causes, both silent:
2268
+ **Settle it mechanically, first, every time — one command:**
2269
+
2270
+ ```bash
2271
+ vidfarm motion-check ./final.mp4 # exits 1 and says why when nothing in the video ever changes
2272
+ ```
2273
+
2274
+ It samples the video and compares consecutive frames. `vidfarm render --target local` now runs it automatically and **fails the render** on a frozen output (`--allow-static` for a title card that is meant to hold still). Run it by hand on anything that did not come out of that command — a cloud render, a watermarked master, an ffmpeg re-encode.
2183
2275
 
2276
+ Three real causes, all silent:
2277
+
2278
+ - **The composition assigned an engine-owned global.** `window.__player` and `window.__hf` belong to the engine; assigning either destroys the frame-capture bridge, and the renderer then stalls 45 s, downgrades the failure to a warning, and writes a correct-length MP4 of a still image with exit code 0. **Four paid deliverables shipped this way in one night.** `vidfarm lint` and `vidfarm qa` both call this an error now, and a local render refuses to start. Never write those globals — see SKILL.md § "Reserved globals".
2184
2279
  - **A watermark/overlay pass without `-loop 1` on a single-frame PNG input.** The frame-sync collapses the whole video onto one frame. Five videos shipped this way before it was caught.
2185
2280
  - **Assets outside the composition root.** Only `<style>`/`<script>` *inside* the `data-composition-id` root execute, and sibling relative files may not resolve — fonts, images, even the animation library itself. The timeline never starts; frame 0 still renders fine because frame 0 is the static DOM.
2186
2281
 
2187
- **The rule that catches both: always compare two frames from different scenes.** They must differ a lot. And when you've applied any pass over an existing video (watermark, overlay, dedupe, re-encode), also compare each output frame against **its own** input frame at the same timestamp — that difference should be tiny. Two checks, opposite directions:
2282
+ **Then look, too: compare two frames from different scenes.** They must differ a lot. And when you've applied any pass over an existing video (watermark, overlay, dedupe, re-encode), also compare each output frame against **its own** input frame at the same timestamp — that difference should be tiny. Two checks, opposite directions:
2188
2283
 
2189
2284
  ```bash
2190
2285
  # consecutive/distant frames must DIFFER (motion preserved)
@@ -2988,6 +3083,8 @@ vidfarm render "$FORK_ID" --dir ./work --target cloud --tracer "batch-2026-07-09
2988
3083
 
2989
3084
  `--dir` may also point directly at `composition.html`. Local renders don't push `./work` to the cloud — `vidfarm publish` (or the cloud-target render) does that. Render prep automatically downloads a long source once and cuts the timeline's segments from it, so referencing the same long raw N times is fine.
2990
3085
 
3086
+ **A local render is gated at both ends, because a broken composition still produces a plausible MP4.** Before it starts, it refuses any composition that assigns an engine-owned global (`window.__player`, `window.__hf`, …) — that mistake writes a correct-length video of a still image and exits 0. After it finishes, it runs `vidfarm motion-check` on the output and **fails with exit 1** if not one sampled frame pair differs. Pass `--allow-static` when the video is meant to hold still. A cloud render has no such gate: run `vidfarm motion-check <file.mp4>` on the downloaded result yourself.
3087
+
2991
3088
  Best practices:
2992
3089
 
2993
3090
  - Prefer one canonical base fork per automation run, then branch from that fork if you need variants.
@@ -3116,7 +3213,8 @@ The licensed harness also carries the **generative build workflow** guidance (ch
3116
3213
  | `vidfarm remove-video-captions <forkId>` (alias: `ghostcut`) | `GET .../compositions/:forkId/remove-video-captions` | subtitle-removal status |
3117
3214
  | `vidfarm snapshot <forkId>` | `POST .../compositions/:forkId/versions` | save composition fork version |
3118
3215
  | `vidfarm versions <forkId>` | `GET .../compositions/:forkId/versions` | list versions |
3119
- | `vidfarm render <forkId> [--dir <dir\|composition.html>] [--out <path>] [--target local\|cloud] [--wait]` | local (default): renders ON YOUR MACHINE via bundled hyperframes, free; `--target cloud`: `PUT/PATCH working files, then POST .../compositions/:forkId/render` (billed) | script-friendly render to MP4; no `--dir` uses the fork's current working state |
3216
+ | `vidfarm render <forkId> [--dir <dir\|composition.html>] [--out <path>] [--target local\|cloud] [--wait]` | local (default): renders ON YOUR MACHINE via bundled hyperframes, free; `--target cloud`: `PUT/PATCH working files, then POST .../compositions/:forkId/render` (billed) | script-friendly render to MP4; no `--dir` uses the fork's current working state. A **local** render refuses to start on a composition that assigns an engine-owned global, and **fails (exit 1)** when the finished MP4 never moves — `--allow-static` for a card that holds still on purpose |
3217
+ | `vidfarm motion-check <video.mp4> [--json] [--fps N]` | (local ffmpeg, no route) | **does the finished video actually MOVE?** Exits 1 when every sampled frame pair is identical. A frozen render matches on duration, frame count, file size and audio hash and has a perfect frame 0, so nothing else catches it. Runs automatically at the end of `render --target local` |
3120
3218
  | `vidfarm render-status <forkId> <renderId>` | `GET .../compositions/:forkId/renders/:renderId` | poll a render |
3121
3219
  | `vidfarm visibility <forkId> <private\|public>` | `PATCH .../compositions/:forkId/visibility` | set visibility |
3122
3220
  | `vidfarm clone <forkId>` | `POST .../compositions/:forkId/clone` | clone a fork |
@@ -3211,6 +3309,8 @@ What it flags:
3211
3309
  | `layout-template` | error | The frame composed like a **page**: 3+ stacked text blocks in one container ending in a call to action — headline + subheading + CTA, the web hero/modal body. Fires with or without the box around it, because the STACK is the tell. Three stacked lines with no CTA are a legitimate title card and pass |
3212
3310
  | `modal-scrim` | error | A full-frame backdrop that is **blurred AND dimmed** — the website-modal staging that pushes the picture back so a floating block pops. Blur alone passes (the blurred fill behind a 16:9 clip in a 9:16 frame is a real technique); it needs the dimming too, and a bed faded under `opacity:0.25` is an ambient texture wash, not staging, so it passes as well |
3213
3311
  | `gradient-text` | error | `background-clip:text` gradient headline fills |
3312
+ | `engine-owned-global` | error | A `<script>` assigns `window.__player` / `__hf` / `__playerReady` / `__renderReady` / `__hyperframes`. **The only rule here that is about correctness, not taste** — assigning one destroys frame capture, and the render still exits 0 with a correct-length MP4 in which nothing moves. Register `window.__timelines["<data-composition-id>"] = gsap.timeline({ paused: true })` instead. Same check runs in `vidfarm lint` (`engine_owned_global_assigned`) and blocks `vidfarm render --target local` |
3313
+ | `missing-timeline-registry` | warn | A GSAP/anime timeline is built and never registered on `window.__timelines` — the engine has nothing to seek, so the render is a still image |
3214
3314
  | `clickable-element` | error/warn | `<button>`, `<form>`, `<input>`; `<a href>` warns |
3215
3315
  | `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 |
3216
3316
  | `page-structure` / `bullet-list` | error/warn | `<nav>`/`<header>`/`<footer>`/`<table>`; a `<ul>` with visible bullet markers |