Inspiration
Before a single day of shooting, every film production runs a "clearance review": every brand, song, real person, real location, and trademark in the screenplay has to be checked for rights. It typically takes 2-3 weeks and costs $5,000-15,000 and roughly 90% of that time is pure research, not judgment. A paralegal reads 120 pages, extracts 40-60 items by hand, and Googles each one for a rights holder and a copyright status. Independent and mid-budget productions often skip it entirely and absorb the legal exposure later.
That gap a research-heavy, well-defined workflow that a production almost never gets automated help with is what ClearFrame is built for.
What it does
ClearFrame reads a screenplay, extracts every flaggable item, runs sourced research on each one, and produces a review-ready clearance log with every claim traceable to a cited source.
One constraint shaped the entire product: ClearFrame never says an item is "cleared." It surfaces evidence and triages workload for a human reviewer. This isn't just copy it's enforced in the type system (no tier in the schema can be read as a legal conclusion), in a forced rules gate the model cannot skip, and in a citation validator that drops any claim without a source before it can reach a finding.
The flagship result is a 1926 song mentioned in our sample script. ClearFrame resolves it into two separate rights the musical composition and the sound recording because they have different terms under US law. Sourced evidence puts the composition's publication at 1926, so its 95-year term has expired: public domain since 2022. The Music Modernization Act gives 1926 sound recordings a 100-year federal term: in copyright until 2027. Same song, opposite outcomes, both fully sourced (16 citations, tagged by which right they support) this is the single most common mistake in real-world clearance work, and ClearFrame gets it right without being told to.
How we built it
Stack: Gemini for extraction and synthesis, the Google Agent Development Kit (ADK, no LangChain/LangGraph anywhere enforced by an AST-level test) for orchestration, the Parallel API for sourced web research, FastAPI + Cloud Run for deployment.
Pipeline: ingest -> extract -> research -> assemble -> review -> export.
- A deterministic Python rules engine computes copyright terms public-domain
cutoffs, life+70/life+60 terms, the composition/recording split with zero
LLM calls in that module. If a fact is missing (e.g. a person's domicile at
death, which determines their right-of-publicity term), the engine returns
INSUFFICIENT_FACTSnaming exactly what's missing rather than guessing. - Gemini extracts items with page-anchored occurrences; a deterministic PDF parser is the authority on page numbers, not the model, because a wrong page makes a finding useless to a reviewer.
- Parallel researches each unique item with a category-specific brief a music item gets two separate research briefs, one per right.
- Assembly combines evidence, rule outcomes, and Gemini's synthesis into a
tiered finding. Forced function calling (
FunctionCallingConfigMode.ANY) means the model cannot produce a tier without the rules engine having run first this is a model-level constraint, not a prompt instruction. - A human reviewer confirms, rejects, or escalates every item through a keyboard-driven triage UI before export is allowed enforced server-side with a 409 response, not just a disabled button.
Validation: a 19-item golden set, each entry verified independently by fetching sources fresh, never by re-reading the pipeline's own stored evidence, so a source the pipeline missed could actually contradict it. Final numbers: 100% extraction recall, 94.7% precision, 100% evidence coverage, 100% citation-accuracy, 100% rules-engine accuracy.
Challenges we ran into
The interesting bugs were all the same shape: a silent success that was actually a failure.
- Early on, if every extraction call failed, the pipeline reported
COMPLETEwith 0 items, which reads as "this script is clean" rather than "the pipeline broke." We now fail loudly and explicitly instead. - A rate-limit and a hard monthly spend cap return the same HTTP status. We were retrying the spend cap as if it were transient, which buried the one error message that actually mattered under repeated retries.
- Gemini calls had no retry policy at all (only Parallel did) - a single 429 mid-run silently degraded a finding to an empty rationale with a plausible-looking tier attached. Nothing crashed; the output just quietly got worse.
- Our citation validator's regex matched only one source ID per bracket, so
claims citing two sources (
[ev_a, ev_b]) were discarded as "uncited" - the exact opposite of what the validator was for. Fixing it took kept claims from 20 to 76 on the same run. - The frontend's demo-fixture ID collided with a real report's ID after regeneration. A reviewer could decide all 27 items, watch the counter advance, and open the export gate - with zero requests ever reaching the server. Caught only by a live QA pass, never by a unit test.
- The most serious one: a genuinely
FAILEDpipeline run rendered in the UI as an empty, clean script - with the export button green and enabled. That's precisely the false negative the backend guard above was built to prevent, leaking through at the UI layer instead.
Every one of these got a regression test, not just a fix - several as AST-level checks that scan our own source for the anti-pattern (no LangChain import, no import-time secret reads, no assertion that can print a live key).
Accomplishments that we're proud of
- A rules engine that refuses to guess: it names the exact missing fact instead of fabricating a term, every time.
- The composition/recording split working end-to-end on real, sourced data - not a hypothetical we made up to look clever.
- A tier system where the model cannot mark anything as safe, structurally, not just by prompt convention.
- Independent verification that found and honestly reported two mismatches in our own benchmark (a findable domicile fact our research brief missed, and one wrong-entity resolution) rather than tuning them away before submission.
What we learned
The hardest part of building a trustworthy AI agent isn't getting it to produce a plausible answer - it's making every silent failure mode loud. Every serious bug we shipped was a case where the system kept running and looked fine while doing the wrong thing. The fix, every time, was the same discipline: fail explicitly, log what broke, and never let a degraded run wear the same face as a good one.
What's next
Query-depth improvements to the research briefs (the domicile-fact miss points at a concrete fix), persistent storage for the multi-script dashboard, and extending the rules engine to more jurisdictions beyond the US and India.
Built With
- asyncio
- css3
- docker
- fastapi
- gemini
- github
- google-adk
- google-cloud
- google-cloud-run
- google-cloud-secret-manager
- html5
- javascript
- parallel
- parallel-api
- playwright
- pydantic
- pypdf
- pytest
- python
- rest-api
- tailwindcss
- uvicorn
- vertex-ai

Log in or sign up for Devpost to join the conversation.