Mograph — style-locked motion graphics
The mograph lane turns a script or timed transcript into a fleet of short motion-graphics clips that all share one look. It has two artifacts, four commands, and one principle: style is locked once in a motion sheet; every clip prompt is action-only choreography. The engine stacks style lock + SHOT + AUDIO + AVOID per block at plan time — style text is never re-typed per clip, so the look cannot drift across 100+ clips.
Creative authoring (the sheet interview, block choreography) lives in skills/mograph/SKILL.md. This doc is the command/artifact contract. The design rationale is in docs/design/specs/2026-07-13-mograph-native-port.md.
Artifacts
artifacts/motion-sheet.json (schema: schemas/video/artifacts/motion-sheet.schema.json)
The single style authority for the project's motion-graphics lane:
styleLock— ≤120 words, attached verbatim to every clip prompt. Must contain the layout guard ("do NOT copy the sheet's layout") — without it, clips reproduce the reference board's grid as their composition.negative— must end with the five audio bans (no music, no soundtrack, no voice-over, no narration, no lyrics): clips sit under the operator's own VO, so generated audio is sound design only.family— one of the built-in style families (mograph-sheet --families): paper-editorial, cutout-collage, zine-print, flat-vector, painted-photo, kinetic-type, flat-duotone, glass-dark, blueprint, terminal-neon, soft-3d.refImage— the rendered master style board (versioned; regeneration never overwrites a prior version), plus optionalstage(a persistent background world for one-continuous-take continuity),palette,typeRoles,logoRefs,gotchas.
artifacts/motion-pack.json (schema: schemas/video/artifacts/motion-pack.schema.json)
One per video: coverage rows give every second of the timeline a decision (generated / v2v / talking-head / screen-rec / reuse), and blocks carry the clips — id B###, time range, priority P1|P2|P3, mode (t2v|ref2v|v2v-stylize|v2v-overlay|v2v-transition|v2v-inset), optional VO line / SFX cues / per-block refs / loop flag / v2v videoSource, and the action: pure choreography, ≤90 words, no style vocabulary.
Commands
vclaw video mograph-sheet --project <slug> (--from-json <path> [--write] | --show | --master-prompt | --families)
vclaw video mograph-pack --project <slug> [--init-from <srt|vtt|whisper-json> --video "<title>" | --check | --stats | --assemble <id> | --list]
vclaw video mograph-render --project <slug> [--priority P1|P2|P3] [--block <id> ...] [--route <route>] [--enqueue | --plan-only] [--emit-batch <path>] [--write-sidecars <dir>] [--stitch]
vclaw video mograph-logos --project <slug> --brand <name> [--brand <name> ...]- mograph-sheet validates and persists the sheet (fail-fast on a missing layout guard, missing audio bans, over-budget style lock, unknown family, undescribed locked stage).
--master-promptcomposes the deterministic art-direction-board prompt — render it viavclaw video gen-imageat the DELIVERY aspect (providers inherit aspect from the attached reference). Alockedsheet refuses overwrite without--force. - mograph-pack seeds a coverage-only skeleton from a timed transcript (pause-aware ~5s beats), lints the pack (the anti-drift gate: hex codes and style words in actions, unquoted or over-long on-screen text, SFX naming music, v2v without a source, duplicate ids, coverage gaps), prints scope stats (P1 / P1+P2 / all, with
--cost-per-clipestimates), and assembles any block's exact submitted prompt. - mograph-render plans or creates a durable render DAG. Refs are transport-aware: on the Omni family (
veo-useapi) IMAGE refs are OMITTED and the clip renders prose-only from the style lock (an attached ref hijacks the on-screen text — validated across ~20 pilot renders; the plan surfacesrefs-omitted-omni), while Seedance-family routes keep the sheet ref. It lint-gates the pack, then either:- default /
--enqueue(canonical durable queue): compiles every selected provider render with its exact prompt, ordered references, source, route and lineage. Each render owns a dependent zero-costlocal-mographpost-process task that verifies output bytes before writing the clip and sidecar.--stitchadds one local assembly task after every post-process task and writes a hash-bound master receipt. Provider renders remain quote/authorization gated and execute only throughcinema-work. --plan-only: inspect the exact contract without writing queue state.--emit-batch(legacy-compatible export): writes a batch manifest; new submissions return to the canonical queue throughbatch-submit --project.bashReferences ride in the manifest'svclaw video mograph-render --project demo --priority P1 --route runway-useapi --emit-batch out/p1.json vclaw video batch-submit --manifest out/p1.json --project democharacterRefs(the provider REFERENCE slot, never the first frame). v2v blocks are excluded from the manifest explicitly.--write-sidecarscan emit planning sidecars immediately; durable post-process tasks always write verified delivery sidecars underartifacts/mograph-sidecars/.
- default /
- mograph-logos fetches real brand marks (keyless: Clearbit → SimpleIcons → favicon fallback) into
projects/<slug>/assets/logos/. Attach the file as a block ref and prompt "the attached <brand> logo, flat, unmodified" — a model drawing a mark from memory produces gibberish letterforms.
Gates — what refuses to render, and why
Two lints stand between a pack and spend. Both are fail-fast: an error exits non-zero and blocks the render; a warning prints and proceeds.
The sheet is validated at RENDER time, not only when written. It is a file on disk, so it gets hand-edited after the write — and four defects reached delivered clips through that gap. Every render plan goes through one choke point, so the durable DAG, --plan-only, --emit-batch and sidecars are all covered. Read-only paths are deliberately NOT gated: --show reports the issues alongside the sheet (it is the command you reach for when the sheet is broken), and --master-prompt composes the board from palette/type/stage and never reads styleLock.
| Code | Sev | What it catches |
|---|---|---|
style-lock-not-descriptive | error | A lock that names the family instead of describing it (<35 words of real description, guard clauses stripped so boilerplate cannot pad the count). On the default route image refs are stripped, so this prose is the ONLY style instruction the model gets — a lock reading "use the attached style sheet" shipped a 3D render for a flat-vector film. |
style-lock-hex-code | error | A hex code in the lock. The model reads it as copy to set: #F5B72E and #1B2A4A were typeset into frame as labels. Name the colours; the board pins the values. |
style-lock-over-budget | error | Lock over 120 words. It rides above every block, so every word is paid for N times. |
style-lock-layout-guard-missing | error | No "do NOT copy the sheet's layout" clause — without it clips reproduce the board's grid as the composition. |
style-lock-sample-words-guard-missing | warning | Only bites routes that keep the board ref (Seedance family); its sample copy bleeds in as on-screen text. |
negative-audio-bans-missing | error | The negative must end with the five audio bans — clips sit under the operator's own VO. |
block-verb-repeated | error | Consecutive blocks that add no motion the previous one did not already use. A pack could "pop up … settle" eleven times and pass everything else. Verbs match on word forms: the register writes "tapes down", action prose says "tape down", and matching the register's spelling literally left the gate silent on real content. |
block-verb-unrecognised | warning | A block whose action uses no verb from its family, so the variety check could not evaluate it. Raised for every block including the first. |
action-text-slots-unspecified | warning | The action builds more copy-bearing components (label bars, caption strips, stat tiles) than it gives words for. An unspecified slot is not blank on the generator — the model fills it from the nearest vocabulary it has, which is the style lock riding above every prompt. A pack asking for three label bars with one quoted string rendered two cards reading QUIET MONO MONO, straight out of the lock's "quiet mono labels", and it passed every other gate. Quote every slot, or declare the spares positively ("blank label bar"). |
family-components-underused | warning | Fewer than 4 of the family's components appear anywhere in the film. |
Route notes
| Route | Use for | Notes |
|---|---|---|
runway-useapi (batch default) | free overnight drafts | explore lane queues; low res |
seedance-direct | free/paid finals via the configured adapter | reference budget ≤9 images |
dreamina-useapi | paid hi-res finals | Seedance-family text rendering is mid |
veo-useapi (omni-flash) | Omni and v2v queue tasks | best on-screen text + native SFX; ref-free (image refs omitted — prose-only) |
prompt-only | always free | sidecars to paste into any tool |
Ref-free is Omni-only. Seedance-family routes KEEP the sheet ref (they lock identity differently) but are weaker at on-screen text — keep text-heavy P1 cards minimal there, or render them on Omni. After rendering, the standard finishing lane applies: assemble --from-clips, media-qc, the preview portal, make-vertical / burn-subtitles.
Deterministic engine (default for flat brand graphics)
For FLAT brand-token motion graphics (solid grounds, cards, pills, icons, typography), skip AI-video rendering entirely: the deterministic PIL engine bundled at skills/mograph/scripts/deterministic/ draws every frame as code and pipes rawvideo into ffmpeg — pixel-perfect text, exact brand hexes, real composited logos, procedural sample-accurate SFX, and 16:9 / 9:16 / 1:1 masters from one codebase, free and reproducible (~3 min per aspect, local). The provider routes above are for organic looks (texture/light/depth) and v2v work. See the engine's README for the workflow and the PIL/ffmpeg gotchas; the doctrine and decision rule live in skills/mograph/SKILL.md.
Worked example — first production run (validated 2026-07-13)
The full lane, exactly as first exercised on a real project (paper-editorial-explainer, paper-editorial family, 16:9, locked stage, calm-premium):
# 1. Project + sheet (interview happens in the mograph skill; JSON is the output)
vclaw video init paper-editorial-explainer
vclaw video mograph-sheet --project paper-editorial-explainer \
--from-json sheet.json --write # ok:true, 0 issues
# 2. Board image: compose the prompt, render via gen-image, review, then lock
vclaw video mograph-sheet --project paper-editorial-explainer \
--master-prompt --aspect 16:9 # 408 words (400-600 window)
vclaw video gen-image --project paper-editorial-explainer \
--prompt "<master prompt>" --kind overlay --aspect 16:9 \
--model openai-gpt-image-2 --out .../assets/MG-PAPER-ref_001.png --confirm-spend
# operator approves the board -> write refImage {path, version:1, approvedAt}
# + locked:true back into the sheet JSON and --write again
# 3. Pack (outline route, timing: draft) -> lint -> stats
vclaw video mograph-pack --project paper-editorial-explainer --check # 0 errors
vclaw video mograph-pack --project paper-editorial-explainer --stats
# 4. One pilot block through the free lane
vclaw video mograph-render --project paper-editorial-explainer \
--block B001 --route runway-useapi \
--emit-batch batch/pilot-b001.json --write-sidecars artifacts/mograph-sidecars
vclaw video batch-submit --manifest batch/pilot-b001.json --out batch/pilot-run
vclaw video batch-monitor --out batch/pilot-run --once # repeat until terminalOperational notes from that run:
--kind overlayfor the board render — screens/overlays keep text; the board's type specimen NEEDS its text.openai-gpt-image-2is the house model for multi-panel boards with heavy typography; the first render passed QC (crisp specimens, correct palette hexes, no gibberish) with zero iterations.- Expect an explore-lane throttle on first submit.
batch-submitonrunway-useapican return"throttled": true, submitted: 0, pending: 1— the job is safely queued locally, and the nextbatch-monitor --oncepass pushes it through when the throttle clears. This is normal, not a failure; the explore queue itself then takes hours (it is the free draft lane). - Lock order matters: persist the sheet WITHOUT
refImagefirst, render and review the board, then writerefImage+locked: truein a second--write. A locked sheet refuses further overwrites without--force. - Outline route packs (
timing: draft, hand-authored coverage + blocks, no--init-from) lint the same as transcript-seeded packs — the gate does not care where the beats came from. Re-map times and fliptiming: finalonce the real VO lands.
