A
?
← Handbook

SOP — the video editor

Run a cut of an ad from footage to a Frame.io version. The tool is video-pipeline/; its architecture, stage list and gate list live in video-pipeline/README.md and are not repeated here. This file is the procedure.

Before anything

Render on the droplet, never on a laptop. root@188.166.225.209, key ~/.ssh/atlas_video_worker, code at /opt/atlas/video-pipeline. The droplet is not a git checkout; code gets there by rsync. The laptop disk has filled twice on video and the render is CPU-bound anyway.

Nothing Alex watches is a half-res or correction-only draft. He reads flatness and softness immediately. Drafts are for checking structure; anything shown to him goes through 11-finish.sh, which is full resolution with the look pass. The receipt records look and fullRes so this is checkable rather than claimed.

Frame.io is never deleted from. See ../rules/frameio-never-deletes.md. The client allows GET, POST, and PATCH for two narrow things. If a stray upload needs removing, leave it and say so.

Before you cut anything

These come from the OLOID post-mortem, where nine versions were abandoned. Each one cost a day. Full account: ../knowledge/oloid-post-mortem-what-the-editors-do.md.

  1. Open the reference before the footage. If the client has delivered work in the corpus, look at its contact sheet. If there is a delivered cut of the same script, look at that first. One frame of the editors' cut answered questions that two days of guessing did not. The 785-video library is the reference, not a deliverable.
  2. Use every footage folder you were given. Never exclude one in a log line. "Old" or "not maintained" describes the index, not the footage: on OLOID that judgement discarded 141 B-roll clips, 9 consultation clips and 22 person clips, and left a 23-clip pool that could not reach the standard.
  3. Never cover the speaker's head. A still that overlaps an A-roll segment gets a talent matte drawn back over it, so the photograph sits behind him. Stage 07 requests this; MatteLayer draws it.
  4. Check who is speaking. node tools/check-speaker.mjs <project> measures pitch per beat against the talent's baseline. A crew member reading a line to the talent matches the script word for word and passes every text check; she was 218 Hz against his 137.
  5. Speed is per beat, not per read. Measure the speech rate of each beat and speed that beat. A single global rate tightened a hook that was already tight and left the body slow.
  6. Coverage and cut rate are separate decisions. "Cover more of the talking head" is not "cut faster". Ask what each shot is doing before setting a shot length.
  7. A rule you apply without judgement will produce a worse cut. Once-per-source is right, until it forces a cut-in out of a beat that simply needed a different shot length.
  1. Sound design is a layer, not a default. assets/sfx/bank.json catalogues 64 effects by the moment they serve: risers lead into a claim or reveal, hits land on a cut or a word, whooshes cover a picture change and must vary. The OLOID cut shipped twelve copies of one pop and nothing else.
  2. Check the voice chain on a gap, not on the speech. Measure mean volume in a pause and during a phrase. If the separation is under about 15 dB the room tone is riding up and the ad will sound unclean however good the words are.

Checks that run before a render

Check Catches
tools/check-speaker.mjsa different speaker on a beat
script coverage, in stage 07the brief's words missing from screen
caption-not-in-brief, in stage 07transcription errors reaching the screen ("phone" for "home")
script-omission, in stage 07copy dropped from the brief without an approver
the post-render audio diffcrew talk or fluffs left in, and slate words

The two commands

# structure check: 540x960, correction grade only, 20-40 s once conform is cached. Never shown to Alex.
bash stages/10-draft.sh <project> <tag>

# what Alex watches: build, conform, look, matte, proof, full-res render, master to -14 LUFS / -1 dBTP
bash stages/11-finish.sh <project> <tag>

Both are safe to re-run: every stage checks for its own artefact first, so the same command is the resume command. Neither touches Frame.io.

Delivering

Delivery is opt-in and separate, because it is the one outward-facing step:

bash stages/11-finish.sh <project> <tag> --deliver                      # new VERSION of the asset in decisions.deliverable
bash stages/11-finish.sh <project> <tag> --deliver --as-master "Name.mp4"  # a separate delivery asset instead

Which of those two is right is Alex's call per project, not the agent's. Ask; do not pick.

The music beat grid is required

Cut placement is decided against the grid at assets/beat-grids/<track>.beats.json, produced by tools/beats.py. It is committed, because a missing grid used to be a warning that silently produced the same manifest with no cuts on the beat at all. A missing grid now fails the build and names the remedy. work/audio/ is the scratch copy and is read first; assets/beat-grids/ is the copy of record.

How cuts land on the beat, and why the window is asymmetric, is documented at the top of snapCut in stages/07-build-manifest.mjs. The short version: a cut may be pulled 0.22 s earlier onto a beat and only 0.06 s later, so the picture never arrives after the line that motivates it, and a cut with no beat inside that window keeps the time the meaning gave it.

What is in git and what is only on the droplet

Thing Where it lives If it is lost
stages, tools, Remotion source, style cardsgitnothing to recover
projects/<id>/ decisions, script, brief, b-roll librarygitthe edit is gone; this is the product
music .wav, sfx, generated assets, beat gridsgitnothing to recover
conform / look / matte / proxy cachesdroplet onlyregenerable, costs render time
work/out/*.mp4 renders and mastersdroplet onlyregenerable from the manifest
corpus artefactsgit, see the corpus SOPmachine-hours and API spend

The rule behind that table: anything a decision was made in goes to git. Anything a machine can rebuild from those decisions does not, because it is media and media does not belong on the laptop or in the repo.

Traps already paid for

  • pkill -f <pattern> matches the ssh command line and kills your own remote shell. Use the bracket trick ("[1]1-finish") and put the kill and the relaunch in separate ssh calls.
  • A render that logs SIGKILL is not trustworthy even when it exits 0. 11-finish.sh checks for this and fails; lower RENDER_CONCURRENCY if it fires.
  • Never open a browser tab that plays audio while working on this. Alex has hunted for the sound on his own machine.
  • Three whisper processes is the maximum on the 8 GB droplet, and only when nothing else is rendering.