# Record a run

A recording plays a finished run back fast, on the real canvas, and writes it
out as an MP4 you can drop into a blog post or a feed. The run is fixed: every
step, timing, output, vote and cost in the video comes from the run's own
history. What you direct is the camera: which steps it stops on and for how
long. The waits in between are squeezed to a pause.

Only a finished run can be recorded (completed, failed or cancelled). The
recording is made from its history after the fact.

## Open the editor

On a finished run's page (`/runs/$id`) the header has a **Record** button. It
opens the recording editor at `/runs/$id/record`.

A run you have already directed shows **Edit recording** instead and reopens
your storyboard.

The editor is three things around one preview:

- The preview at the top left plays the recording. The transport under it
  scrubs, plays and pauses.
- The list on the right is the run, one row per step in the order it ran.
  Each row offers two moments the camera can dwell on, the step's start and
  its result (a gate's waiting and its decision), and a beat is one of those
  moments switched on. Roscoe suggests the beats from the run itself (a human
  overriding a judge that passed, a step the run survived failing, a gate
  decision, the outcome) and says why beside each one. Turn a beat off, or
  turn any other moment into one. The rows themselves cannot be added,
  removed or reordered: that would be editing the run.
- Select a beat and the inspector under the preview sets which rail tab it
  shows (inputs, outputs, exchange, trace) and how long it holds. With
  nothing selected the same box shows the pacing panel: the target length,
  the floor and ceiling a busy step may play for, and the pause left where a
  wait was skipped.

Every change saves on its own. The direction lives beside the run as
`<ROSCOE_HOME>/recordings/<runId>/storyboard.json`. Nothing about the run is
written there.

## Export

**Export…** in the editor's top bar opens the export dialog. It shows the
video's length and a size estimate. The file is always MP4, H.264, 1920×1080
at 30 fps.

The **Video** switch picks the colour scheme. The video is made in the scheme
the app is showing unless you pick the other.

**Export** renders it. The dialog shows the render's progress.

When the file is written the dialog offers **Open** and a
reveal-in-file-manager action, with the file's name and a copy-path button.
The file lands beside the storyboard, named `<workflow>-<scheme>.mp4`.

Two things to know before you post one:

- The video is rendered with Roscoe's own engine, not the browser you are
  looking at, so text can sit a hair differently from the editor preview.
- The video shows each step's output. If a step's output holds something you
  would not paste into a public post, it is in the frames. Watch the file
  before posting it.

### The first export on a machine

The first export downloads Roscoe's rendering engine, once, 99–120 MB
depending on your platform (roughly twice that unpacked) into
`<ROSCOE_HOME>/browsers/`. The dialog does not ask first: it says video export
is being set up and shows the progress. Then it renders. Cancel during the
setup and nothing is kept; the next export
starts the setup over. Once the setup has finished it stays: cancelling or
failing a render afterwards never removes it, and later exports go straight to
rendering. To remove the engine, delete `<ROSCOE_HOME>/browsers/`.

The download honours `HTTPS_PROXY` (and `https_proxy`, `HTTP_PROXY`,
`http_proxy`). Outside the web app, `POST /api/recordings/:runId/export`
downloads nothing unless the body carries `acceptDownload: true` (see the
[REST API](/docs/reference/rest-api#recordings)).

### When export will not start

The **Export…** button is greyed out, with the reason in its tooltip, only
when this machine cannot render at all: Linux is not supported yet, there is
no engine build for this kind of computer, the data folder cannot be written
to, there is no room for the engine, or the engine on this machine cannot
encode H.264.

An export can also fail partway. The ones you are likely to meet:

- The web fonts did not load. The app loads its fonts from a CDN, and a
  render with the fallback font would look wrong in every frame, so it stops
  instead. Check the connection and retry.
- The engine could not be downloaded: offline, or a proxy or firewall
  refusing `storage.googleapis.com`.

The dialog says what went wrong in one line and keeps the raw output under
**Details** for a bug report.

## What is in the file

Everything on screen is run data. The run clock in the title bar shows real
run time, and visibly jumps when a wait is collapsed, so a two-minute run that
plays in thirty seconds never pretends to have taken thirty seconds. Node cards
show their real status, attempts and cost. The rail shows the real input,
output or trace of the step the camera is on. The closing card names the
outcome, and the step it stopped on if the run failed or was cancelled.

Fan-out children (consensus, round-robin, map) are the one place the picture
is coarser than the run: the store keeps no usable per-child timing, so every
child's verdict lands when its parent does rather than staggered.

The fixture the recording is built from is an allowlist projection of the
run: `RunFixture` in `packages/schema/src/recording.ts` carries the run row
without its identity fields (no workflow path, project home, branch, commit,
host or machine snapshot), plus the node rows as `GET /api/runs/:id/nodes`
returns them, with each row's working directory removed. The playback masks
each row's settled values until the moment the run reached them
(`apps/web/src/record/engine/project.ts`).

## Where to next

- [Web UI](/docs/running/web-ui) — the run page the Record button lives on.
- [REST API reference](/docs/reference/rest-api#recordings) — the recordings
  routes an export goes through.
- [Environment variables](/docs/configuration/environment) — `ROSCOE_HOME`, and
  the one rendering-engine override.
