Render Jobs
Last Updated: 2026-07-21
A render takes about a minute and its Lambda runs fire-and-forget, so the web app
needs somewhere to say what a render is doing. That is the RenderJob node.
This page covers its status machine in apps/web/src/lib/neo4j/render-jobs.ts
and the render helpers in apps/web/src/lib/flights/. The UI that starts these
jobs is the Flight Deck; the model they render is the
overlay pipeline.
The status machine
RenderJobStatus (in render-jobs.ts) is:
QUEUED → PROXYING → AWAITING_SYNC → RENDERING → ENCODING → COMPLETE | FAILED
COMPLETEandFAILEDare terminal (isTerminal).AWAITING_SYNCis the parked-on-a-human state (isParked): nothing is running and nothing is wrong — the job is waiting for the owner to scrub the video and mark the liftoff frame.setRenderJobLiftoffis the single guarded transition out of it (guarded onstatus = 'AWAITING_SYNC', which makes a double-click harmless).- The two middle states exist because a browser cannot decode a raw camera file
(Motion JPEG in AVI):
PROXYINGbuilds a small H.264 scrub copy, andAWAITING_SYNCis the job sitting still until a human marks the frame. An upload the browser can decode skips straight toAWAITING_SYNCwith no proxy. ENCODINGis a real status in the machine — the render's progress-reporting phase betweenRENDERINGandCOMPLETE. (The read path also runs a watchdog: a non-terminal, non-parked job untouched for 20 minutes is reportedFAILEDat read time, since a Lambda that was killed mid-run can't write its own failure.)
What the record carries
RenderJob keeps, in plain terms:
sourceKey— the uploaded source;proxyKey— the browser-playable scrub copy (onceAWAITING_SYNCon the proxy path);outputKey— the finished video (onceCOMPLETE).liftoffVideoTimeS— where liftoff sits in the source video,nulluntil the user marks it.units(metric|imperial) and the render selection:overlayVariantId,chunkId, and — only for acustomchunk —chunkT0/chunkT1(the named chunks resolve their window from phase math at render time, so they carry no scalars).progress(0–100), a user-safeerrorwhenFAILED,requestedBy(the authorisation boundary — a job is only visible to its requester), andexpiresAt(mirroring the +30-day S3 lifecycle).
The keys stay server-side: the client-facing shapes expose booleans
(hasOutput / hasPlayable) and a human title, never a key it could presign.
Reads return null/[] on no match; writes throw on failure — except
fetchRenderJob, which is deliberately not wrapped in cache() because its
whole purpose is to observe a value that changes during a request (polling).
Render helpers (apps/web/src/lib/flights/)
overlay-variants.ts— the app-side passthrough to the lib's overlay-variant catalogue (getOverlayVariant/listOverlayVariants), so call sites depend on one app path rather than reaching across the workspace boundary.render-selection.ts— maps a picked(variant, named chunk)into the render-start fields (resolveRenderSelection).unitsis derived from the variant (the twofull-band-*variants encode it), never an independent toggle. The v1 picker only emits a named chunk ({ id });customis API-only.render-job-title.ts— pure "video × variant × chunk" label formatter (renderJobTitle), e.g.onboard.avi × Full band (metric) × Boost, composed from the source-key basename and the variant/chunk labels. Touches no keys or ids the client shouldn't see.phase-markers.ts— derives a flight's phase-marker times (flightPhaseMarkerTimes) and the named chunks it can offer (availableChunkIds), using the samebuildPhasePresetsavailability rule as the chart toolbar.NamedChunkIdexcludescustom.