The audit they watch
An SEO audit that arrives as a branded film instead of a forty page PDF. Three answers from you, then Claude researches the site, designs the scenes, narrates the findings in three voices and renders the whole thing on your Mac.
Use /groundslate-seo-audit-video and the SEO audit plugin on <<YOUR SITE>>, against <<COMPETITOR SITE>> and <<COMPETITOR SITE>>. Brand from <<YOUR BRAND SOURCE>>. It is for <<WHO WILL WATCH IT>>. Bring me the draft.
Works with
- Claude, with the Marketing plugin's seo-audit skill
- The agent this skill is written for. The research runs Claude's seo-audit skill, shipped in the Marketing plugin: in Claude Code, open Plugins, Browse plugins, Marketing. Any SEO audit skill already installed can stand in. The findings arrive through Claude; the captures are GroundSlate's own, rendered by WebKit on your Mac under a site policy only you can widen.
- Claude Design, or any design tool that outputs HTML
- Designs the page and holds the brand. GroundSlate renders the page to video and can keep it as a named kit. What it ships is tooling: a ruler for placing marks against text, a browser library of transition moves, puppet engines for 2D and 3D characters, and a console kit that holds a video to what a classic games console could draw. It stores no brand and designs nothing itself.
- ElevenLabs, or any voice or music model
- Generates the audio, outside GroundSlate. Your agent generates it through its own connection to the provider, an MCP server or an API key, and hands the file back. It stores the voice id against a character and mixes, ducks and lays whatever your agent brings back. Most work needs no provider at all: GroundSlate speaks scripts locally in a designed or stock voice, and writes instrumental beds and sound effects locally too. A provider is for a specific voice you own, or a catalogue track you want by name.
Nobody reads the audit. The findings are right and the report is thorough, but it dies in an inbox because it looks like homework. The same findings as an eight minute branded film get watched to the end, forwarded upward, and remembered in the meeting where the budget is decided.
This skill makes Claude the whole production. The seo-audit skill from Claude's Marketing plugin does the research (any SEO skill you have installed can stand in). Claude Design builds the scene system from your brand. GroundSlate renders it deterministically, narrates it in three local voices, ducks the music under them and files the finished film. You answer three questions, approve two style frames, and review a draft of the film. Everything else has a default.
Everything in <<double brackets>> is yours: your site, the competitors to measure it against, where your brand lives (a Claude Design project, a brand skill, or a folder of assets), and the audience the tone is pitched at. Fill them in before you paste.
To bring the brand in from Claude Design, send a second ask:
Use the claude_design MCP (https://api.anthropic.com/v1/design/mcp, auth via /design-login) to import this project: <<YOUR CLAUDE DESIGN PROJECT URL>>
Install it
Copy or download SKILL.md above.
For Claude Code, save it as ~/.claude/skills/groundslate-seo-audit-video/SKILL.md.
For claude.ai and the Claude desktop app, put it in a folder named groundslate-seo-audit-video, zip the folder, and upload it under Settings, Capabilities, Skills.
The research step wants an SEO skill beside it. Claude ships seo-audit in its Marketing plugin: in Claude Code, open Plugins, Browse plugins, Marketing, and it runs as /seo-audit. Or keep whichever SEO audit skill you already use.
The skill
---
name: groundslate-seo-audit-video
description: Turn an SEO audit into a branded, narrated video. An SEO audit skill (the seo-audit skill from Claude's Marketing plugin, or whichever is installed) does the research, Claude Design builds the scene system from the brand, and GroundSlate renders every scene deterministically, speaks the narration in three local voices over ducked music beds, and files the finished film. Use when the user wants an SEO audit, site review or competitor comparison delivered as a video.
---
# The SEO audit, delivered as a film
You run the whole production. An SEO audit skill is the researcher: use
the seo-audit skill from Claude's Marketing plugin when it is available, or
whichever SEO audit skill is installed.
Claude Design is the art department, GroundSlate is the render engine, the
voice and the library, and the user answers three questions at the start,
approves the look on two style frames, and reviews one draft at the end.
Between those moments, proceed on the defaults
and your own judgement; ask only when an answer would genuinely change the
film, a site behind a login, a brand font that cannot be found, a finding
too sensitive to narrate unchecked.
## Three questions up front
1. The site, and up to a handful of competitors to measure it against.
2. The brand: a Claude Design brand kit, a brand skill you carry, a folder
of brand assets (logo, fonts, colours), or nothing, in which case you
derive a clean system from the audited site's own look. The brand may be
the client's or the agency pitching them; ask whose name goes on it, and
hold the logo file before you author a scene. When an agency is making
the film about a client's site, get both logos, the client's for the
findings and the agency's for the close. The brand lives with you
and Claude Design, never in GroundSlate; GroundSlate keeps the mechanics
(fonts in its font library, the logos in its asset store, approved pages
as named kits).
3. Who watches it: the client's leadership, your own team, or a prospect.
That sets the tone and how technical the narration gets.
Everything else is defaulted, and stated once when you accept the job:
1920x1080 at 30fps, eight minutes, narrated by three distinct
local voices, music beds across the whole film ducked beneath every spoken
line, filed in a project named for the site and the quarter.
## Preflight, before any promise
- The profile first. get_context, then confirm the profile with the user
by name before anything is created: the project, the imports and the
film all land in the active profile, and the user can switch profiles at
any moment. Once confirmed, pass profile= on every library call, never
on machine-wide ones (the queue, voice_status, media policy).
- composition_doctor: the render path (Chromium, ffmpeg) proves itself now,
not at scene twelve. Run the printed fixes if it reports gaps.
- voice_status for the narration engine; relay its fix sentence verbatim if
weights are missing.
- Fonts: import_fonts on the brand's font folder once, list_fonts for the
families the library now resolves, then check_fonts on each page once it
is written. render_html refuses missing fonts rather than substituting, so
resolve them before authoring.
- Brand media: import_asset every logo the film carries (both of them when
an agency is presenting a client's site) and any supplied imagery into
the profile's asset store; get_asset resolves them to paths at build
time. Files must sit under a granted folder before import.
- The project: search_projects for one named for the site and the quarter,
and create_project (kind business) when it is not there. Hold its prj_
id for delivery and pass it on everything that binds, not only the final
film; commit_to_library attaches to an existing project and refuses an
unknown one, so the binder exists before anything renders.
- The project is not just where the film lands, it is where everything the
film is made of and everything it makes lives. Renders go to Scratch
Outputs, which may be deleted, so anything the production created and did
not bind is in a folder that does not survive the quarter. Three doors, by
kind. Media pulled off the web binds as it arrives: pass project_id to
fetch_asset and it is a member in one call. Media you created during the
production, a cutout from remove_background, a frame you pulled from a
clip, binds in two: index_image makes it a catalog row and
add_media_to_project binds it, and that result carries `unresolved`, which
is non-empty when a reference matched nothing and was therefore never
added, so read it rather than assuming. Finished renders go through
commit_to_library with the project. Final outputs only there: a draft scene
render is throwaway and rebuilds from its own page, and committing thirty
of them buries the film they were drafts of.
- import_asset is a different thing and the two are easy to confuse. It puts
a file in the profile's asset store so a render can call it by name, which
is reuse across every project rather than membership of this one. A logo
can want both. Doing one is not doing the other.
- Text has its own door, and this production makes a lot of it. The audit
findings, the script, the per-scene briefs, the chart data and the
continuity note are working material: they are not media to bind and not a
render to commit, and until there was somewhere to put them they lived in
the conversation and died with it. write_project_file keeps them in the
project's own folder, under an area prefix (`notes/`, and the areas
list_project_files reports back). Ask the one question the tool asks: will
you need this file if the conversation ends right now? The audit you spent
an hour researching answers yes. Write it before you design a scene from
it, and the next session opens with read_project_file instead of running
the research again.
- project_manifest before you plan the cut, and again when a render cannot
find its footage. It copies nothing and it answers with per-drive online
status, so the report is "the Sony drive is unplugged and holds twelve of
these clips" rather than twelve paths the user has to interpret. Collecting
a project into an export bundle is the user's own action in the app, on the
project screen; the manifest is the whole of what an agent may do here and
it is the half that matters before a render.
## The audit is yours; the pages are GroundSlate's
Run the SEO audit skill (the Marketing plugin's seo-audit when available,
otherwise the one installed) against the site and competitors: keywords, on-page,
technical, content gaps, backlinks, the full sweep. Harvest the sites
while you are there: a finding about a page shows that page on screen,
not a graphic standing in for it. Target at least sixty graphics assets
pulled from the pages of the site and its competitors, page screenshots,
hero shots, product photography, logos, icons, textures, all saved under
a granted folder. Two doors bring them in, and which one you use
depends on where the file came from. A file that already exists on the web,
a logo, a press image, product photography, an illustration, comes in
through fetch_asset with its URL and the project's id: it downloads,
indexes, becomes a searchable clip and joins the binder in one call, and the
source receipt records where it came from. Never route a found file through fetch_media, which is for media you
generated at a provider and claims a generator that does not exist for a
photograph somebody else took. Screenshots you captured yourself are local
files, so those go through index_folder as before. Bind everything to the
project with add_media_to_project, and import_asset the ones scenes will
reference by name, the logos and the background plates. GroundSlate captures pages itself: `capture_page` for a still and
`record_page` for footage, WebKit on your Mac, cookie banners refused at
capture time. Prefer a real capture of the page over a graphic that stands in
for it, and prefer `record_page(scroll=True)` over a still you then have to
animate. It signs in to nothing on its own, so a page behind a login needs
`open_browser_window` and the user, and capture answers to their site policy
which only they can change.
Leave fetch_asset's `recognize` off, which is its default. A site's news
page is full of photographs of real people, and enrolling the client's
staff or a competitor's executives as identities in this profile's People
catalog is not something an audit should do on its way past. Fill in what
you actually know on the receipt, the page you found it on, what the page
called it, the credit and the licence, and leave the rest empty rather than
inventing it.
The harvest is the film's art department, and it is spent, not stored.
Every asset you capture earns its way onto the screen: as the evidence in
a scene, as the plate a background is built from, as a mark, an icon or a
texture. Every scene carries captured imagery; a flat panel or an invented
graphic standing in for a page you already have is the harvest wasted, and
it is the difference between a film about the site and a film of it. Keep
the tally as you author, the asset, the scene it lands in and the moment it
appears, and account for it at the draft. An asset that never reaches the
screen is either placed or it was not worth capturing, and you say which.
Then distill. A film carries fewer numbers than a report, so choose the ones
that move a decision:
- One headline verdict the cold open can land in a sentence.
- A ranked handful of findings per area, each with the number, the evidence
and what it costs the business.
- Quick wins separated from strategic work, because that split is the plan.
- Competitor deltas stated as gaps to close, not tables.
## The film: a scene system, not a slideshow
Cold open with the verdict. One scene per finding area. At the midpoint
the verdict returns, restated as the gap the plan will close, timed to a
bed change so the film audibly turns toward the answer; eight minutes
needs a second hook. Quick wins as a rapid sequence. The plan. A close
that names the next review.
Have Claude Design build the scene system from the brand: a title scene, a
stat scene, chart scenes, an evidence scene with a screenshot panel, a
list scene. Consistency of palette, type and logo treatment is what makes
it read as designed rather than generated; inside that system, vary
everything else so the film never settles:
- One statement per scene, set large. The scene's argument is a single short
line at a size you could read from the back of a room, and it is the only
thing on screen the viewer is asked to read. Cut it until it fits that
size; a line that will not cut is two scenes.
- The film never sets prose small. Small type is only ever verbatim
evidence: a filename, a tag, a heading lifted off the page, a column
pulled straight from the source. Evidence is looked at rather than read,
which is why it can be dense. Seven identical filenames stacked in
monospace make their point as a shape, before a word of it is read, so set
it aligned and repeating with the part that matters marked, and the
density becomes the argument. A sentence set small is the failure this
rule exists to stop, and a stack of them behind bullet points is a slide.
- If a line is worth saying but not worth setting large, the narration says
it and the screen stays quiet. Most small type on a scene is the voice's
job leaking onto the picture.
- Fill the frame. A layout that leaves half the frame empty is type set too
small, not a frame too big.
- Motion is how a thing arrives and leaves, never a state it lives in. Type
enters, settles, and is then completely still until it goes. Nothing
breathes, floats, bobs or drifts while a viewer is reading it.
- The layout is fixed before the first frame. Every element the scene will
ever show has its position from t=0, and an arrival is opacity and a short
move into a slot that was already there. Nothing already on screen moves
because something new appeared. A list that creeps upward as each line
lands is moving the lines a viewer has just started to read, and it is the
most common way a scene comes out feeling like it is drifting.
- One thing moves at a time, and it is whatever the narration is on. While
the statement lands, the evidence is still; while a row of evidence is
marked, the statement is still.
- Small type does not move at all. It cuts or fades in place, then holds.
The still-type rule governs type; pictures are the layer that moves.
- Composition varies between scenes, never inside one. A full-bleed word
here, a corner-anchored stat there, an oversized numeral bleeding off the
edge: that is one decision per scene, made before the first frame and
held, so no two neighbouring scenes compose the same and neither one
wanders.
- Imagery has many jobs and a blurred plate behind type is the weakest of
them. Every scene decides how its captures appear, and the treatments are
a vocabulary to spend rather than one move repeated: the page as the
subject, full frame and sharp, scrolling or pushing into the headline
under discussion; a capture held in a browser or device frame, angled and
sliding; two competitors' pages side by side with the difference between
them visible; a grid of many captures at once where the repetition is the
finding, fifty-six identically named images shown as fifty-six thumbnails
rather than described; a detail crop magnified out of a page with the rest
ghosted around it; a product shot or a mark cut off its background and
composed into the frame; a capture revealed through the type, or the type
knocked out of it. The blurred plate is the fallback, not the default. The cutout is
remove_background: Apple's subject lift, on device and near instant, and it
writes a transparent PNG at the source's own resolution with the subject
still in its original position, so it drops back over the untouched
original as a layer. Stills only, a video is refused rather than looped.
Check `foreground_fraction` on the way past, because near 1.0 means almost
nothing came off and near 0.0 means it kept a speck, and an image with no
subject writes nothing rather than handing you an empty cutout. The edge
carries a soft halo at very large sizes, so a cutout is right for a
composite, a layout or an overlay and wrong for a silhouette held full
frame.
- Vary the treatment the way you vary the chart forms, and keep a record of
which scene used which. No treatment carries two scenes running, and a
film where every capture ended up behind the type as wallpaper has thrown
away the harvest even though every asset technically appeared.
- When the narration names a company, that company is on screen while it is
named, as itself: their page, their mark, their product shot, sharp and
legible and in the foreground. A viewer hearing about Puma should be
looking at Puma. A blurred texture of somebody's photography behind a line
of type is not that, and it is the difference between showing the field
and decorating a slide about it.
- Type is still, pictures move. That is the whole motion rule stated once.
When a capture is the subject, the frame moves over it as a function of t,
a push into the detail, a scroll down the page, a pull back to reveal
where it sat, a rack from one crop to the next, and the type holds
perfectly still against it. When the statement is the subject, the type
arrives and the picture holds. A plate behind type drifts slowly, and that
slow ground under still type is what reads as expensive, while type doing
the drifting is what reads as a template.
- A scene whose ground is flat colour is a scene that ignored the capture
sitting in the asset store.
- Treat the captures rather than pasting them. list_effects is a catalog of
named looks for one clip, grade, stock, print, glass, bloom and camera
moves, each row carrying its own knobs and its cost, and apply_effect bakes
the chosen one onto a file. A capture graded into the brand's palette and
sitting under a glass or print treatment reads as photographed; the same
capture dropped in raw reads as a screenshot in a deck. Call list_effects
first and pass only the knobs on the row you picked: a knob that is not on
that row is refused, not ignored. Hold one look across the film the way a
film holds one stock, and do not spend a different treatment per scene.
There is a second catalog, list_ai_effects, that runs neural models per
frame for depth and subject work. It is genuinely different and genuinely
slow, it needs models the user installs themselves, and each row carries a
measured cost per frame: quote that cost before you apply one to anything
long, and default to the ffmpeg looks.
- Mix the chart forms, and let the shape of the data pick the form rather
than habit. A share of a whole is a pie or a donut drawing itself round.
A ranking is bars, but a ranking that changed is a slope or a bump chart
between two dates. A count is a waffle grid filling in, or one huge
numeral. A spread of competitors is a scatter or a positioning map.
Coverage is a treemap or a grid filling cell by cell until the hole is
obvious. A funnel is a funnel, a hierarchy is a tree, a change over time
is a line drawing itself in. Where none of the standard forms fits the
finding, invent the graphic the finding needs; a bespoke picture of one
specific problem is the most memorable thing a film like this can carry,
and two bar charts in a row is the report again.
- Build charts out of the harvest wherever the data has a face. Competitor
marks are the points on a positioning map, favicons are the units in a
count, real thumbnails are the cells of a coverage grid, and a page
screenshot is the thing a callout points at. A chart made of the actual
brands beats the same chart made of coloured rectangles, and it spends the
assets in the same move.
- Evidence scenes show the real page, and the page obeys the motion rules
too. Scenes are HTML, so the capture drifts, zooms to the problem or
scrolls the page as a function of t, and the mark draws itself on as the
evidence is named. A screenshot parked in a static panel is the one place
the film goes dead. A captured page is evidence, so its own
text is not the film's type and does not answer to the rules above. The
framing carries the load instead: hold it on the line that matters, so
what a viewer reads is the claim and not the whole page.
- Nothing stays on screen for more than ten seconds. The clock is per
element and it starts when that element appears, not when the scene does,
so a headline that arrived at t=2 is due at t=12 even though the scene
runs to twenty. Any element still there at ten, a chart, a screenshot, a
line of type, gets replaced, reframed or moved on, and a scene that has
nothing to change at ten seconds is two scenes.
- And nothing leaves before it can be read. A line of type stays up at
least as long as it takes to say it aloud, and nothing a viewer needs
exits mid-word. The ceiling keeps the film moving; this floor keeps it
legible.
Pace the film as a curve, not a metronome. The cold open is the fastest
sequence in the film, evidence scenes get room to breathe, the quick wins
come rapid-fire, and no two adjacent scenes run the same length.
Scenes carry designed content only: the number, the evidence, the words
being narrated. No numbering of any kind, no slide or scene numbers, no
"finding three of seven", no progress dots, no section labels, no footer
with the site name on every scene, no caption that names the chart it sits
over. That furniture is a deck labeling itself; this film's structure lives
in the narration and the design.
No em-dashes in anything the film sets. Not in a headline, a stat label, a
strap, a chart label or a card. A comma, a colon, or two lines, and most
lines that reach for a dash wanted to be two lines anyway. At headline size
an em-dash is a bar of dead space across the frame, which is why the rule
bites harder here than on a page. Verbatim evidence is the exception and is
never edited: a captured page, a quoted tagline or a line lifted off a
competitor keeps whatever punctuation its author used, because the moment
you tidy it, it stops being a quotation.
The film is an argument, so its data is honest. Every number the narration
speaks is the number the screen shows at that moment, and both trace back
to the audit; a chart's scale never flatters or exaggerates a gap. The
first inflated bar a viewer catches costs the whole argument.
Every scene is a self-contained HTML page obeying the render contract:
window.__duration (or data-duration) set to the scene's planned length, and
window.__seek(t) that places the visual state exactly at time t. The picture
must be a pure function of the timeline: no clocks, no live randomness, no
fetches, data baked into the page. Charts draw as functions of t, so a
counter at t=3.0 shows the same digits on every render, and the kinetic
type obeys the same law: every entrance, settle and exit is computed from
t, never from a running animation clock. That determinism is what makes
drafts honest and re-renders safe.
## Three voices hold the clock
- Cast three voices from list_voices before writing a line: a lead who
opens the film, lands the verdict and closes with the plan; a second who
reads the findings and the evidence; a third for the competitor deltas
and the quick wins. Audition with preview_voice; the contrast between
them should be audible inside one sentence.
- Budget the script before writing it. The local voices read at
documentary pace, roughly 140 to 180 words a minute, so eight minutes
holds around 1,100 to 1,300 words across the whole film. Weigh the
scenes inside that budget; a script that ignores it is a twelve minute
film discovered after the narration is synthesized.
- Write the script scene by scene, marked for who speaks, as spoken prose:
full sentences with subjects and verbs, the way a documentary narrator
talks, never labels or fragments read aloud.
- The narration never reads the screen. The screen carries the number; the
voice tells the story around it, who is losing what, and what changes
when it is fixed, with the data as the proof. Say the number, then what
it costs: "Four of the ten money pages load in over five seconds. Slow
pages are the first thing a buyer leaves." A spoken number always
matches the one on screen; a mismatch is a bug, in the scene or in the
script.
- Speak it with synthesize_speech at temperature 0, one file per scene in
that scene's voice.
- Then time the picture to the words. transcribe each narration file with
word timestamps and use them as the scene's cue sheet: the number lands
on screen as it is spoken, the mark on the screenshot appears when the
evidence is named, the next line of type waits for its words. Set each
scene's duration from the timestamps, the last word plus a breath, then
stretched to the bed's next cut point so the scene changes on the music. This
is where the ten second rule is won or lost: the words decide the length,
so a line of narration that runs sixteen seconds has just bought a
sixteen second scene, and that scene owes the viewer a change of picture before
ten. Plan the change with the cue sheet rather than discovering it at the
render.
- Fix the fit before you render. Too loose, dead air after a word while the
screen waits, trim the silence out of the file with render_ffmpeg and
re-transcribe. Too tight, a visual with no room to land before the next
line, rewrite the line shorter or give the scene the extra second. The
cue sheet is only done when nothing on screen leads or trails its words.
## The soundtrack is one mix
- Eight minutes is too long for one track, so the film carries four or
five music beds, each owning a stretch of it: one for the cold open, one
or two across the finding areas, a faster one under the quick wins, a
closer for the plan. Secure them when you accept the job, not at the
mix. `generate_music` writes them here, one brief per bed, instrumental,
with the seed on the record so a bed the user liked comes back exactly.
Tracks the user supplies work too, and your own music connection is
there when you carry one. If `generate_music` reports that the model is
not downloaded on your Mac, relay that sentence and stop: the download
is the user's to start in the app.
- The beds are the film's other clock. Run audio_beats on each one before
timing a single scene and cut on its suggested cut points, the strongest
downbeats, never on every beat: scene changes, transition moves and the
type's biggest hits all land on the grid of whichever bed is under them.
Change beds at area boundaries, on a cut point of the outgoing track, so
the music turning over reads as the film changing chapter. With no beds,
the words alone hold the clock.
- Scenes render silent, so the soundtrack is one continuous mix: narration
laid at each scene's offset, each bed laid over its stretch and ducked
under every spoken line with mix_audio, restored in the gaps, the argv
run through render_ffmpeg, and the finished track handed to the join as
render_sequence's audio_path, one track laid across every seam.
- A transition overlaps its two scenes rather than adding time, so the film
runs shorter than the scenes do by the length of the moves. Take the total
off render_sequence with plan_only, which renders nothing, and build the
mix to that number rather than to the cue sheet.
## Render, join, deliver
- Style frames before the film. Render the cold open and one evidence
scene at draft and show them before authoring the rest: a wrong
direction costs two scenes, not thirty. Read them against the rules while they
are cheap to fix: one statement set large, no prose set small, a fixed
layout with nothing creeping, nothing sitting past ten seconds, and
captured imagery in every layer. A scene
system that breaks those at the style frame breaks them thirty times over.
- Render every scene with render_html at draft while iterating: identical
frames and timing, only compression differs, so the draft is the honest
review copy.
- Where a run of scenes belongs together, author it as one page rather than
as separate files joined afterwards. author_film takes the scenes and the
seams between them and writes the whole self-contained page in one call:
the stage, the absolutely positioned scenes, the transition kit wiring,
`window.__duration`, `window.__seek` and `window.__READY__`. None of that
is a creative decision and every hand-written line of it is a chance to
break the seek contract, which is the failure that renders cleanly and
looks right until somebody counts the frames. The seams inside that page
are the free ones. Use it for the quick wins sequence and any run that
shares a ground; keep render_sequence for the joins between the areas of
the film, where a rendered move is the point.
- Join the scenes with render_sequence: the scene files in order, and one
transition entry per seam, N-1 of them or it refuses. Give the seams inside
a sequence "cut" and the area boundaries a move that matches the film's
energy, so the moves keep meaning. One call encodes every frame once,
however many scenes there are, and quality is a tier there too, so join at
draft while the film is still moving.
- Stay in the light tier. Thirteen of the thirty-one moves are cost: light and
the other eighteen run a per-pixel kernel at five to thirty seconds a join,
paid again on every draft pass. The whole dynamic family is heavy, so spend
one at a couple of real boundaries rather than at every one, and read cost
off list_transitions rather than off a name. There is a second library that
runs inside the page and costs nothing to join, browsable with
list_transitions(engine="html") and vendored with
get_kit("transition kit"); a scene turn is free there and heavy here.
- Sample frames off the joined film and look at them before you call it done.
A path cannot show you a substituted typeface or a finding sitting on the
wrong scene; a frame can.
- The full draft is the big checkpoint: show the user the draft film with
the script. Findings change wording here, scenes reorder here, and
nothing has been spent on final quality yet. Bring the accounting with
it: the assets that reached the screen and where, the ones that did not,
and any scene that had to set prose small, with what the narration could
have said instead. List the treatment and the graphic form each scene used
while you are there, and the longest any single element stayed up: two columns of that list is where a film that thought
it was varied turns out to be one move repeated. A rule nobody checked at
the draft is a preference.
- On approval: re-render scenes at high, re-join at high with the mix as
audio_path,
commit_to_library into the project, name_video with the site and the
quarter, and confirm with list_project_outputs. What that list should
show is the whole body of work, the captures, the assets the production
made and the film, not one file with everything it was built from left
loose in Scratch Outputs.
- save_kit the approved scene pages as a kit under the
client's name, and add_project_note a continuity note beside it: the
three voice ids, the beds, and the quarter's headline numbers. Next
quarter's audit is the same three questions rebuilt in the same look and
the same voices, with a delta chart drawn from recorded numbers rather
than memory.
## GroundSlate renders the web itself
This is the art department for anything that lives on a site, and it beats
sending a human for screenshots.
- `capture_page` is a still, rendered by WebKit on your Mac. `device` is
desktop (1440x900) or mobile (390x844 with a phone user agent, so the site
serves its real mobile layout). `full_page` for the whole scroll, `scale`
for retina. For a 9:16 still use `device="mobile", width=360, height=640,
scale=3`, a true phone viewport upscaled rather than a squeezed desktop.
- `record_page` is VIDEO of a page, and `scroll=True` walks it top to bottom
across the recording. That is what turns a static site into footage. Record
at 24fps: a scrolling page repaints continuously and holds 7 percent of its
frames at 24 against 16 to 26 percent at 30, measured.
- `consent="reject"` dismisses a cookie banner by REFUSING it, at capture
time, so the banner is never in your plate. There is no accept option and
there should not be one.
- `session="shared"` uses the durable signed in session from a window the user
opened, so a page behind a login can be captured. `open_browser_window` puts
a real window on their screen with your `reason` written above it, for a
login or a verification step. Nothing here solves a challenge for you and
nothing should try.
- Capture is off until the user turns it on, and then answers to their site
policy. `browser_policy` reads it. `request_browser_site` ASKS, with your
reason, and the user decides in GroundSlate. You cannot grant it, and asking
twice increments a counter rather than creating a second request.
Use it for the real artefact rather than a recreation. A film about a website
should contain that website, moving, with its own type and its own
photography, not your drawing of it.
## Show them, every round
A round of work ends when the human has seen it, not when you believe it is
good. `request_review(source, ask, agent=, profile=)` puts the file in front
of them with a timeline they can comment on, regions they can draw and a
verdict bar. Do this at the end of every round, unprompted. Describing a
render in prose is the thing the tool exists to replace.
- ALWAYS pass an `ask`. With one, the window carries Approve and Request
changes. WITHOUT one it is a SHOW: it still accepts notes and offers no way
to submit them, so a human can write thirty notes into a window that cannot
send them. Use a show only to put an asset in front of somebody with nothing
to decide.
- ALWAYS pass `agent=`. Unnamed, you are recorded as the person: nothing is
reused, `withdraw_review` refuses everything including your own, and the
app's raise skips your review entirely.
- READ `placed` BACK AND SAY WHAT IT SAYS. `opened` means the window is coming
up, `waiting` means the app is running but not frontmost so its Dock icon is
bouncing and the review is on the Dashboard, `saved` means the app is shut
and it will be there next launch. Telling somebody to take a look when their
app is closed is exactly what that field exists to prevent.
- Poll `review_status` for the verdict and the notes. Park on `review_wait`
only when the verdict genuinely blocks the next step; it is a blocking wait
on a human.
- One live ask per file. `withdraw_review` takes back a superseded question of
your own. It refuses a review that has been decided, one carrying comments,
and anybody else's, which is correct: never delete somebody's writing.
Every note carries an anchor: `t`, sometimes `t_end`, and a `region` with a
normalised box plus a `where` in words. Work from the anchor rather than your
reading of the sentence. Map each note to the scene that owns that moment and
write the map into the project with `write_project_file` before you start, so
the round survives you.
Then report per note, one block each, DONE or NOT DONE or PARTIAL, with the
evidence. A claim is not evidence. "Added kinetic text" is a claim; "probe
returned clean, rendered 562 frames, the three lines land at 2.94, 4.10 and
6.84 inside the safe area" is evidence. If a note cannot be done, say NOT DONE
and why, and never report a note done that you did not verify. Ask for the
next round the same way.
## Everything happens inside GroundSlate
The tools run on the user's Mac, in the background, on a queue. They are
faster than you and they cost none of your context. Every minute you spend
hand rolling a filtergraph is a minute the machine was idle and your context
was being spent on something nobody can read afterwards. Reach for the tool
first, every time.
- Authored pages: `write_composition` writes your HTML to a real path the
renderers can open, and vendors sibling files beside it. Before it existed
an agent authoring scenes had to reach for a filesystem bridge outside
GroundSlate. Do not go back to that. `preview_html` to check it,
`render_html` to bake it.
- Assembly: `render_sequence` takes N clips and N-1 seams in one call and
encodes every frame exactly once. `author_film` expresses scenes and seams
as one page composition. Both enforce the identity below; a hand rolled
concat does not.
- Ducking: `mix_audio` IS the broadcast sidechain. Pass voice and music, get
`ffmpeg_argv` back, hand it to `render_ffmpeg`. Note the one parameter that
is not in decibels: `threshold` is a linear amplitude because that is what
the compressor takes, and a dB value is refused rather than misread.
- Effects: `list_effects` and `apply_effect` for the graded looks,
`list_ai_effects` with `preview_ai_effect` before `apply_ai_effect`. The AI
effects run on models the user installs in the app, Models then Effects;
until they are there `apply_ai_effect` answers `effect_not_ready`, so say so
and stay with the graded looks.
- Transitions: `list_transitions` catalogs both engines, the ffmpeg one and
the page one (`engine="html"`), and `transition_motion` gives you a move's
actual curve and what it drives. Choose on the MEASURED `seam_seconds`, not
the `cost` tier, which is wrong in both directions at the edges.
- The web: see the capture section. GroundSlate renders pages itself now.
And do not bake early. A film that exists only as a finished mp4 has to be
rebuilt in full for a one word change. Keep the edit as data for as long as
you can, and bake the deliverable last.
## The project is the source of truth, and the test is a second agent
Everything this production learns lives in the project, not in the
conversation and not in a scratch directory. The test is concrete: a
DIFFERENT agent, tomorrow, given only the profile and the project id, must be
able to pick the work up and continue it. If they cannot, the production
failed, however good the film is.
- `create_project` early and hold the `prj_` id. Pass it to everything that
binds, not only the final render.
- Media from the web binds as it arrives: `fetch_asset` with `project_id`.
Media you made binds in two steps, `index_image` then
`add_media_to_project`, whose result carries `unresolved` for references
that matched nothing. Read it rather than assuming.
- Text is working material and it has its own door. The brief, the script,
the per scene cue tables, the note map from each review round, the voice
ids, the measurements you took and the ones that surprised you: all of it
goes in with `write_project_file` under an area prefix. `add_project_note`
records a decision AND the reason for it, which is the part a later agent
cannot reconstruct.
- `project_manifest`, `list_project_files` and `read_project_file` are how
that agent resumes. Write for them.
- `commit_to_library` the finals only, with the project. Renders live in
Scratch Outputs, which may be emptied.
Anything you leave only in the conversation is gone when the conversation
ends. That includes the reason you chose a voice, the cue table you measured,
and which of the client's notes you decided not to act on and why.
## Timing is frames, and frames are the currency
- A transition OVERLAPS its two shots, it never adds time. A cut runs
`sum(clip frames) - sum(seam frames)`. Do the arithmetic in frames.
- Declare durations that land on whole frames at your fps. 18.15 seconds at
30fps is 544.5 frames and floors to 544, one short of what you meant, and
nothing tells you.
- ONCE AUDIO IS PLACED AGAINST SCENE STARTS, A DURATION CHANGE MOVES EVERY
SCENE AFTER IT. Freeze scene durations before you place a single cue, or
accept that you will re-derive every cue downstream by hand.
- Verify frame counts with `ffprobe` on the delivered file, not from the job
record. Jobs can vanish from a shared queue without writing a file and
without a terminal state.
- Anything that passes through `minterpolate` needs an explicit constant frame
rate normalisation afterwards. Its timestamps do not land on the frame grid,
and the next step drops frames without saying so.
- Keep the whole film one pixel format and one colour range. A single
full range clip among limited range ones is a black level jump at that
splice, and a frame count will never show it.
## Motion has a floor, and a still is not a scene
Both of these are measured, not aesthetic.
- A drift below about 0.3 px per frame emits IDENTICAL frames. A ground
crushed to near black cannot register motion whatever it travels. Clear
8 px per second of visible travel on a ground you can actually see, and
check the render for repeated frames rather than assuming.
- Text over a moving background is not a moving scene. The plate drifts, so a
freeze check passes, and it still reads as a slide with video wallpaper.
The test: IF YOU PAUSED THE PLATE, WOULD THE SCENE STILL BE MOVING? Measure
the type and graphic layers on their own, separately from the background,
and know which of your scenes have grounds that are not tagged as plates,
because those flatter the number.
- Generated video has its own failure modes worth knowing before you spend an
hour of GPU on them. A ping pong loop built by reverse and concat returns to
its first frame on a fixed period and reads as a loop however slow it is.
Bright emissive elements, neon, filaments, blown highlights, smear and plume
frame to frame and look like smoke. Naming camera equipment in a prompt gets
the equipment rendered into the shot. And a generated plate almost always
needs a blur before it stops reading as generated.
- Prefer authoring the camera move yourself over asking the model for one. A
scale driven from `t` is exact, adjustable without regenerating, and cannot
smear. A model's camera move is whatever it decided.
## An instrument that cannot fail is not evidence
- Prove the red before you trust the green. Run your check against something
you know is broken and confirm it reports the fault. A checker that skips
elements with children will pass every headline in the film.
- Whisper is a reconstructive model. Its language model restores the plural it
expects, so a transcript will tell you a word was spoken when the audio does
not contain it. Never use a transcript as evidence for an inflection, a word
ending, or anything acoustic. Measure the acoustics.
- Beware a default that is indistinguishable from a real answer. It passes
every test and is wrong exactly where it matters.
- When two instruments disagree, the one that can be wrong in the direction of
the common case is the suspect. Look at the thing itself; looking settles
cases that measurement cannot.
- Measure the population the claim is about, and confirm on a different axis
than the one that produced the claim.
## Report like a producer
- While the audit and renders run in the background (job_events), keep
building; never make the user wait on a progress bar you could have
worked through.
- Say what resolved and what did not after every batch step; unresolved and
found false are answers.
- End with where the film is, what it is called, and the three findings that
matter most, in one sentence each.