
Video Director
Script in, shot list out — the one artifact the camera, the screen recorder and the editor all read. Decides the package mode, names the engine on every row, quantizes generated-clip durations to the lengths the models actually produce, and lays out a timeline that accounts for transition overlap, so the video does not end in black. Cuts follow a seam rule in the renderer's own transition vocabulary. Every voiceover line is timed per beat against a measured speech rate for 7 languages; a beat that does not fit goes back to the writer with the number of words that do. Fills a @di-atomic/video-analyst template with no hand edits, splitting each section by its measured pace, and prints every departure the engines force with the template's sample size, handing the worst back for a decision. Five verifiers print PASS/FAIL with numbers before any render is paid for. 5 recipes + 14 registry items + 7 references. guidance-only — agents reach it via skill_read.
I built this because the shot list was the one artifact in my video pipeline that nothing produced and everything downstream needed. It turns a script into a list the renderer accepts unmodified. Transitions overlap their neighbours, so clips that add up to 60 seconds render 57 seconds of video and 3 of black: it solves the timeline for content and quantizes generated clips to the lengths the models make. A voiceover line is heard over one shot: 11 of 35 beats in my own 60-second demos ran past theirs, so it times every line per beat from speech rates I measured in 7 languages and hands a long beat back with the word count that fits. It also fills a video-analyst template without hand edits. Mapped one row per section, my one real template broke four gates; split by its own pace it fills to 24 rows, and the two places an avatar cannot keep a presenter's 1.6-second cut come back to me as a decision, marked as one video's habit, not a rule. Five verifiers print PASS or FAIL with numbers before any render is paid for. It renders nothing and writes no words.
Features5
Emit a shot list the renderer accepts unmodified, with an engine and length on every row.
Solve the timeline for content, so transition overlap never ends your video in black.
Time each line against its own shot and return the word count that fits.
Use speech rates measured for EN, DE, ES, FR, HE, RO and RU, not one English constant.
Fill a video-analyst template unedited, and hand back what the engines cannot make.
Videos1 video
DemoVideo Director · Demo video
Demo · VimeoArticles about Video Director2 articles
Your script fits 60 seconds. Two lines don't. Your agent checks.
opvs.ai20 Sep 2026Your script fits 60 seconds. Two lines don't. Your agent checks.
A script can fit its total and still overflow a beat. @di-atomic/video-director times every voiceover line against the shot it plays over, in seven measured languages. New in 0.3.0: hand it a template measured from a video you liked, and it plans yours to that pace and hands back what the AI engines cannot shoot.
Videos1 video
DemoVideo Director · Demo video
Demo · VimeoArticles about Video Director2 articles
Showing all 2
SKILL.mdthe file your agent reads
SKILL.md loads when you open this tab.
What's new6 releases · newest first · from the package's CHANGELOG
v0.3.02026-09-28Latest
The template reader. The Director now fills a `@di-atomic/video-analyst` template with no hand edits, and makes
every place the engines cannot follow it visible and gated. Minor: a new optional input shape, a new optional
top-level block and a new builder script. `scenes[]`, `mode`, `engine` and the arithmetic contract are
unchanged, and a list without `template_fill` gets exactly the 0.2.1 verdicts.
**Validated on ONE template** (Linear, "Intro to Linear", n=1, strength `single`). Every template-derived line
prints its sample size, so a one-video hint cannot be quoted as a rule.
### Added
- **`scripts/fill-template.mjs`, a builder (not a gate).** Reads an AgentDocs template page (its one json block),
its markdown, or a `template@1` file. Refuses zero or two json blocks, a wrong schema, a strength that does not
match `n_videos`, and a `director_input` block on its own. Splits each section by its own measured pace,
clamps rows to the engine (avatar/ai 5–15 s, any row 0.5–60 s), stretches a sub-floor section to the floor and
takes the time from the elastic rows in proportion. `--target`, `--transition`, `--engine`, `--cutaway`,
`--accept`, `--refresh`. Exit 1 when it hands something back.
- **`verify-shot-list.mjs` check 9, template departures.** Runs whenever a list carries `template_fill`.
Re-derives from the rows: section pace < 1.2× INFO, 1.2–2× WARN, **≥ 2× handed back** unless the target length
alone explains it (then WARN, "compressed by the target length"); a broken measured majority opening rule
handed back, judged or minority rules WARN; a length ≥ 1.2× from the template's WARN.
Re-reads the template from the path the block records (or `--template`) and FAILs a block that was edited or
has gone stale.
- **`verify-vo-fit.mjs` dead-air band** (fills the slot 0.2.0 reserved). With `--template`, or a list carrying
`template_fill`: words per minute of the script against `0.728 × min(template vo_wpm, the voice's own pace)`.
WARN at most, same language only, post_batch skipped, `n` and strength on the line. `vo_wpm` never enters the
ceiling chain.
- **`scripts/_template.mjs`**: the template reader and the one departure derivation, shared by the builder and
both gates so they cannot disagree.
- **`template_fill`** output block (`video-director/template-fill@1`): every template number as
`{value, from, label, n, strength}`, the Analyst's labels carried verbatim.
- **`fillTemplate`** discovery method in `schema.yaml`; `planShotList.template` now declares the full
`template@1` shape (nested properties) and still accepts the 0.2.x `director_input` shape.
- **Peer:** `@di-atomic/video-analyst >= 0.1.1`.
- `references/template-fill.md` · `recipes/fill-a-template.md` · registry item `template-fill-linear` (the real
fill, the resolved fill, both gates' output) · 10 learnings (21 total).
### Changed
- `references/voiceover-timing.md` §3: the reserved slot is now the dead-air floor. §7: dead air is WARN with a
template, INFO without one.
- `recipes/tutorial-explainer.md`: pacing is graded when a template is supplied, at that template's strength.
- `references/output-contract.md` §8 and `instructions/DEPLOYMENT.md`: the `template_fill` block and the
Analyst → Director input contract. `instructions/AGENTS.md`: rules 13–14.
### Measured on the one real template
- one row per section: **4 violations** across 2 shipped gates
- filled: 24 rows, 0 structural violations. Hand-backs: hook pace **3.1250×** and `first_shot_change_by_3s`
(measured). WARN: promise 1.3158×, context 1.5000×, recap **1.9997×**, `text_on_screen_by_3s` (judged).
- `--engine hook=motion`: 0 hand-backs; the four structural gates PASS (the fifth, `verify-vo-fit`, runs once the
writer has added the lines); `face_by_3s` (judged) now WARNs.
- `--cutaway context`: 1.5000× → 1.0001×.
### Deferred
- The quantized all-AI duration solver (0.2.0 deferral): its own card. `ai` sections in a template are untested.
---v0.2.12026-09-27
Card patch. No change to the shot-list contract, the arithmetic, the rate table or any verifier. ### Fixed - **`media.demo_video_url` restored.** 0.1.2 added the 20-second launch film; 0.2.0 was seeded from 0.1.1 and dropped it, so the live card lost its demo. It is back, and this manifest now says to preserve it. - **The 0.1.2 entry below is restored** to this changelog for the same reason. - **Card copy rewritten to the card spec.** `long_description` is 188 words, first person, with the numbers (was 298 words and used "our"); 5 features, each 90 characters or fewer (was 7, several longer). ### Added - **A fourth card panel, the clock:** a voiceover line that runs past its shot (`vd-5-clock-hero.jpg`).
v0.2.02026-09-26
The clock. The shot list now checks that every voiceover line fits its row, per beat, before any audio exists. Minor: new optional fields and a new verifier; `scenes[]`, `mode`, `engine` and the arithmetic contract are unchanged. **One deliberate break:** a 0.1.x list whose captions carry no `estimate` flag now fails `verify-projectability` — unflagged words are trusted downstream as aligned, which is the defect. A 0.1.x list without captions passes the original four gates unchanged (`start_s` is optional). ### Added - **`scripts/verify-vo-fit.mjs`, the fifth verifier.** Reads a shot list, a copy-engine beat table or beat JSON. FAILs a beat whose estimated speech exceeds `len − outgoing overlap − 0.1s` (0.25 s on the last beat, the same tail as `@di-atomic/video-editor`), with `over_s` and `max_words` per FAIL beat. FAILs caption estimates under 1.5 words/s or without an `estimate` flag, and short-form speech that starts after 2.0 s. WARNs (never FAILs) on a late first sentence, a sentence over 6 s (provisional), and `...` used as a pause. `--tts` swaps the estimate for measured speech; `--emit-captions` writes rate-derived caption estimates; `--rate` takes a measured brand rate; `--json` emits `vo_fit`. - **`registry/speech-rate-table`.** Spoken tokens per second, measured on edge-tts for 7 languages: EN 2.38 (35 demo beats), DE 2.07, ES 2.53, FR 2.69, HE 1.76, RO 2.28, RU 1.80 (20 lines each), p25 for FAIL, p50 typical. One engine, one voice per language; stated in the item. - **`references/voiceover-timing.md`** and **`recipes/fit-vo-to-the-clock.md`**. - **`checkVoFit`** discovery method; `lang`, `voice`, `tts_rate` inputs; `scenes[].start_s`, `captions.words[].estimate` and `vo_fit` outputs. - **7 learnings**, including *a script can fit its total and fail a beat* and *a verifier printed a dead prototype's defects on every run*. ### Fixed - **`verify-projectability.mjs` printed three false warnings on every run** — "music does not duck", "captions do not arrive", "audio stripped" — citing a prototype that never shipped. The live worker (v1.6.0) renders voice, word captions and a music duck. Replaced by the live downstream facts; the check now fails a dead-prototype field, a non-numeric `duck_db` and unflagged caption estimates. Same correction in `output-contract.md` §1/§5, `DEPLOYMENT.md`, `AGENTS.md`, `SKILL.md`, `README.md`. - **`timeline-arithmetic.md` §4 called `...` "controllable pacing".** Measured: +0.20 s on edge-tts. Pauses are now `[pause Ns]`, realised per engine at handoff. - **Caption estimates were spread evenly over each row** (the 0.1.x test: 0.91 words/s, up to 11 s off). They now come from the speech rate and carry `estimate: true`. - **`start` dropped half-second overlaps** (`0:07` for a row at 7.5 s). `start_s` is canonical; `verify-shot-list.mjs` checks both against the arithmetic. - **Both registry shot lists re-timed** to pass all five gates. ### Measured, and what it changed - On our own six 60-second demo scripts, estimate mode catches **all 11** overflowing beats and flags 5 of the 23 that fit; measured mode flags exactly the 11. 24 new held-out lines: 24/24 estimated at or above their measured time. - Non-English time on 20 demo lines is **+6…+27 %** vs English, not the +19…+53 % a 5-line sample showed. ### Known limits - Estimate mode over-flags scripts of short common words (a Russian test: 4 flags, 1 real; the real one caught). Every FAIL line now says whether the beat is also over at the typical rate. - All rates come from one engine (edge-tts), one voice per language; translations not native-reviewed. ### Deferred to the next minor - Reading `@di-atomic/video-analyst` templates, with `vo_wpm` as a fill target (never the ceiling). - A duration solver (its inputs include template length ranges). ---
v0.1.22026-09-26
Card patch. No change to the shot-list contract, the arithmetic or any verifier. ### Added - **`media.demo_video_url`** — a 20-second launch film for this skill, hosted on Vimeo. It was made with the chain this skill heads: the shot list from this skill, clips and voice from media-generator, the render on SpiderVideo, the poster from thumbnail, the mix from video-editor. ### Fixed - **Nested `skills/video-director/schema.yaml` said `0.1.0`** while the package was `0.1.1`. It now matches the package version.
v0.1.12026-09-20
Propagation patch. No change to the shot-list contract, the arithmetic or any verifier. ### Fixed - **`cli_help` named a command that does not exist.** The frontmatter read `opvs tools install`; every one of the 31 live Di-Atomic listings uses `opvs-skills install`. Corrected in both copies of `SKILL.md`. ### Added - **Marketplace card media.** `media.hero_image_url` plus three screenshots, rendered once and reused across the launch blog, both listing surfaces and the card. ---
v0.1.02026-09-20
First release. Guidance-only: the agent composes the shot list itself following `SKILL.md`, reached via `skill_read`. No backend, no HTTP-bound methods. ### Added - **`SKILL.md`** — a router over four decisions in order: `mode`, engine per row, the timeline, then every cut. - **5 reference procedures** — `output-contract` · `timeline-arithmetic` · `seam-discipline` · `prompt-grammar` · `engine-routing`. - **3 recipes** — `short-form-single-video` · `post-batch` · `tutorial-explainer`. - **12 registry items** — prompt skeletons (the five universal labels, the keyframe triplet, the keep-constant block, a UGC shape with dialogue), the native clip-length table, the video-type enum with its gaps marked, the transition-to-axis map, the seam technique catalog with its measured numbers, the five-part prompt frame, the anti-slop list, and two complete worked shot lists that pass their own gates. - **4 verifiers** printing PASS/FAIL with numbers — `verify-shot-list` · `verify-projectability` · `verify-seam-ledger` · `verify-prompt-grammar`. - **4 learnings** — declared-but-unread fields · why a duration sum is the wrong arithmetic · the default transition violating the rule · a rich corpus that covered only one thing. - **`instructions/AGENTS.md`** for non-Claude runtimes and **`instructions/DEPLOYMENT.md`** for the four downstream consumers. ### Decided - **`mode` is required with no default**, values `single_video` | `post_batch`, and absence is rejected. The same key means opposite things in the source corpus and nothing labels which; guessing concatenates several intended posts into one video with no error anywhere. - **`on_shortfall` is required with no default** — `reject` | `loop` | `extend_last`. The alternative is a silent loop. - **Timeline arithmetic subtracts transition overlap**: `content = Σ len − (n−1) × transition_seconds`. A plain sum ships black frames. Resolves a question left open in two prior design documents. - **Generated-clip durations are quantized** to each model's native lengths; `screen` and `motion` rows are elastic and absorb the remainder. - **`engine` and `type` are separate vocabularies.** `type` is derived from `engine`, never chosen. - **Captions are emitted as `words[]`**, never as an SRT URL — the renderer reads the former and nothing reads the latter. - **Tutorials are in scope**, and `recipes/tutorial-explainer.md` states in its own header that it is reasoned from first principles rather than corpus-evidenced, with pacing explicitly ungated. ### Known limits - The seam gate grades the **declared plan** — the static half of a two-part check. Velocity, phase and carrier continuity belong to a rendered asset and are not verified. - **Three fields in the receiving pipeline are declared and honoured by nothing**: music does not duck, an SRT URL reaches nothing, and the caption and audio blocks are stripped in transit. All three are emitted anyway and reported with file and line on every verifier run. None is this package's defect and none is worked around by it. - Pacing on the tutorial path is not gated. ### Provenance One upstream borrowed from, under MIT, for duration estimation, pause-aware script splitting and three hard-won constants. No upstream code ships and no bundled media is redistributed. A second project was audited and judged a source rather than a dependency; its engine-agnostic craft rules informed the seam reference, no code was taken, and **nothing here emits GSAP**. Full terms: `PROVENANCE.md`.
Showing all 6








