Inspiration
Clearance is the part of filmmaking nobody puts in the trailer. Before a film can ship, someone has to prove it had permission for everything visible and audible in it: the song on the radio, the painting on the wall, the logo on the cup, the face on the magazine cover. That person watches the whole film and writes it all down.
The failure mode is brutal and specific. You miss one thing, and you find out in the delivery window, when the fix is either a licence negotiated from a position of no leverage or a reshoot you cannot afford. And the things that get missed are almost never in the script. They are the prop somebody brought to set, the poster that was already on the wall of the location, the shirt an extra happened to wear.
So the interesting problem was not "read a screenplay and extract the brands." It was: what about everything that ended up on screen that nobody wrote down?
What it does
ClearCut takes a screenplay, a rough cut, or both, and produces a clearance record you can hand to a lawyer.
Give it a script and it reads every scene. Give it a cut and it watches the footage. Give it both and it reconciles them, separating what was scripted, what shows up in both, and what is on screen with no script reference at all.
On our Artemis production, that split was 15 scripted, 3 in both, and 5 that exist only in the footage. On a real 45-second Nike ad with no script whatsoever, it found five things, including a Gymshark logo on a runner's shirt in the background of a Nike commercial. A competitor's mark, in frame, that no script was ever going to mention.
For every element it finds, it runs research and answers four questions. Where did this come from. What did we read. Who probably owns it. How do you ask them. A case ends up with a candidate rights holder, a licensing route with a real contact, and a stack of cited public sources.
Then it stops. The agent never marks anything cleared, approved, or safe. It writes evidence, and a person records the decision under their own name with their own reasoning. We separate "decided" from "resolved" in the record for that reason: a coordinator saying "I checked this" is a decision, but only counsel approval, a filed permission, an approved replacement, or a false-positive call actually closes a case.
Because ownership changes, you can leave a Parallel monitor on a case. If the public record moves, the case reopens and the decision history stays intact.
At feature length this scales: our Clearance production carries 70 cases and 532 citations, and exports as a printable packet.
How we built it
Five Gemini 3.8 Flash agents, built on Google's Agent Development Kit and running on Vertex AI:
- a screenplay scanner that reads scenes and anchors each candidate to the line that triggered it
- a cut scanner that watches the footage and returns timecoded detections
- a page-to-screen reconciler that matches the two and classifies each relationship
- a dossier synthesist that turns retrieved sources into a rights record
- a copilot that answers questions using only what is in the record
Retrieval is Parallel. Every case goes out through Parallel Search, there is a deeper Task mode when depth matters more than latency, and Parallel Monitor backs the standing watches.
The stack is Bun and Hono for the API, Next.js 16 for the web app, and Cloud Run for both. Project records live as objects in Cloud Storage with a Firestore index over them. Media goes to the same bucket.
One decision runs through everything else. The synthesis agent cites by index into the sources it was handed, never by writing a URL. Ask a language model for a link and it will produce a plausible one. A fabricated citation in a clearance file is worse than no citation, because it looks like diligence.
Challenges we ran into
Research took twelve minutes. Our first version handed every case to a Parallel Task, which is thorough and slow. We measured processor tiers expecting a dial to turn, and found nothing: base 125.7s against core 99.9s on one run, lite 245.4s against base 248.3s on another. Variance swamped the setting. The actual fix was structural. We split retrieval from reasoning: Parallel Search retrieves, and a Gemini agent reasons over what came back. A full production went from 696 seconds to 83.
Two productions sat in "analysing" forever with their research finished. This one was nasty because nothing surfaced an error. Firestore caps a document around a mebibyte, and our 70-case record measured 1,809,126 bytes, so every save was rejected with INVALID_ARGUMENT. The pipeline completed, the terminal write failed, and the production simply never finished. We moved records into object storage and kept Firestore as the index.
The model told us there were no rights holders when it had actually been rate limited. Seventy cases hit Vertex at once and the tail came back 429. Those cases were recorded as "no candidate rights holder could be established from public sources," which is a much worse statement than "we were throttled" - one of them had ten sources already retrieved. Throttled calls now retry with jittered backoff.
Uploading a video failed and told the user "Unexpected token '<'". A 12MB rough cut returned 200 in 0.34s straight to the API and 500 after 65 seconds through the URL the browser uses. Next's rewrite proxy cannot stream a request body. We routed everything through a streaming handler instead.
The progress page looked frozen while it was working perfectly. Two separate causes. Bun closes an idle connection after ten seconds and our heartbeat was fifteen, so every live feed was dropped before it could say anything. And the page only took a project from the initial snapshot event, so the incremental events during a run updated nothing.
Opening a production took fifteen seconds. The server answered in 0.49s; the rest was transferring 1.6MB of uncompressed JSON at 262 KB/s. Research prose compresses about four to one. After compressing both hops, it opens in about 1.5 seconds - with the event stream deliberately excluded, since compressing a stream buffers it and would have silently undone the live progress work.
The page scrolled and we could not find what was overflowing. Nothing visible extended past the viewport, yet the document was 2512px too tall. Tailwind's sr-only is position: absolute, and an absolute box is clipped only by an ancestor that is both positioned and clipping. Our scroll containers had overflow but no position, so all 73 screen-reader labels escaped and sat at their flow position in the document.
Accomplishments that we're proud of
The reconciliation is the part we would defend hardest. Finding brands in a screenplay is a text extraction problem. Finding the five things in the footage that were never scripted is the actual job, and it is the reason a clearance pass takes a human days.
We are also glad we held the line on what the agent is allowed to say. It never clears anything. Every claim traces to a document that was actually fetched, gaps are recorded as gaps instead of guesses, and the human decision is stored with an actor and a rationale that cannot be written by an agent. When being confidently wrong has legal consequences, that restraint is the product.
It also runs on real material, not just our fixtures. A Nike ad pulled off YouTube, a documentary script with its cut, and a 70-case feature script all go through the same pipeline.
What we learned
Measure before you optimise. We were confident the processor tier controlled research latency and spent real time on it before the numbers said otherwise.
The dangerous failures were the silent ones. A rejected write, a throttled model call, a dropped connection - none of them produced an error a user could see. They produced a page that looked stuck, or a case that looked researched and empty. Every one took longer to find than to fix.
And a language model's confidence is a liability in this domain specifically. The whole design ended up organised around not letting it assert things: cite by index, record gaps, refuse to close a case.
Built With
- bun
- ffmpeg
- firestore
- google-adk
- google-cloud
- google-cloud-run
- google-gemini
- hono
- next.js
- parallel
- pdf.js
- playwright
- react
- server-sent-events
- tailwind
- typescript
- vertex-ai
- vitest
- zod
Log in or sign up for Devpost to join the conversation.