Claude cannot open an .mp4.

Everything on this page exists to close that one gap — and to stop it from being closed by guessing dressed as sight.


01 — the actual problem

A filename is not a video.

Ask a language model to judge a render and, absent instrumentation, it reads the filename, maybe a thumbnail, and produces a verdict shaped like analysis. It is not analysis — there is no video in the context, only its name and the surrounding conversation's confidence.

That gap is invisible until it costs something. A single dropped frame is 41 milliseconds — no human eye locates it by watching, and no language model catches it by describing a still. The fix isn't a better prompt. It's giving the model something to actually look at, then making it look.

02 — two lanes, on purpose

Comprehension and inspection ask different questions.

"What happens in this video" and "is there a defect in this video" sound like the same task. They aren't — they need different sampling resolution, and a tool built for one is structurally unable to answer the other.

QA lane

frame-eyes

Runs on Claude's own renders and motion output before any verdict ships. Full-frame extraction — every frame is a candidate, nothing is skipped on the assumption it looks like the one before it.

Samplingevery frame
Built2026-08-18
Scopeour output only
Standing ruleMEMORY rule 34

Comprehension lane

watch

Runs on other people's video — YouTube links, competitor ads, tutorials. Scene-cut sampling plus a word-level transcript, built to say what happened and when, fast, across a video no one asked to inspect frame-by-frame.

Sampling1 frame / scene-cut
Cap100 frames · 2fps
Built2026-08-29
HearingWhisper transcript
frame-eyes — every frame, 1.0s window 41ms flicker — caught watch — scene-cut only, same window same defect — invisible at this resolution

Illustrative reconstruction of the two sampling schemes over the same one-second window — not a literal capture. The gap in the lower track is the whole reason a QA lane exists separately from a comprehension lane.

03 — the verified record

Two real catches, not a hypothetical.

frame-eyes exists because it found things that would otherwise have shipped. Both of these were flagged before delivery, confirmed by reading the extracted timeline and sheets — not by a number alone.

2026-08-18
Single-frame flicker in a Fen ad render, caught before delivery.
3.00s · 41ms
2026-08-19
Frame-rate judder in an already-delivered mnaam master, caught on re-inspection.
22.68fps / 24fps

04 — the judgment step

Numbers flag the place. Eyes decide if it's a defect.

A metrics dump is not a verdict — a deliberate flash-frame trips the same flicker detector as a real defect. So the pipeline never stops at summary.txt.

Extract

The script pulls every frame, renders a timeline.png and per-second contact sheets.

Look

Claude reads the timeline and sheets directly and reacts — cites the frame and timestamp, never "feels abrupt."

Cross-check

A Gemini vision pass judges the same sheet independently before the verdict is called final.

Report

Collapsed events, not raw frame counts — one line per real defect, or a clean pass.

python3 ~/.agents/skills/frame-eyes/scripts/frame_eyes.py <input> -o <outdir>

05 — why a rule, not a hope

Discovery was measured, and discovery is a coin flip.

The cold-skill surfacer is supposed to notice when a task needs frame-eyes and suggest it. It was measured directly against the task "check if this mp4 has flicker."

0.703 relevance score against a 0.47 threshold — a clear match by the numbers — yet the hook only surfaced it on 1 of 3 identical runs. A model this confident should not need to get lucky to remember it has eyes.

06 — the artifact

So it's a standing rule, not a suggestion.

MEMORY.md — rule 34

Any moving image — video, animation, render, GIF, UI motion — runs through frame-eyes first. Read timeline.png and at least one sheet_*.png before saying anything. Never judge motion from a filename, a thumbnail, or one frame.