Skip to content

feat(scripts): flag a capture region that briefly returns to an older picture - #4167

Open
miguel-heygen wants to merge 7 commits into
mainfrom
feat/capture-reversion-check
Open

miguel-heygen wants to merge 7 commits into
mainfrom
feat/capture-reversion-check

Conversation

@miguel-heygen

Copy link
Copy Markdown
Collaborator

What

scripts/check-capture-reversion.mjs: checks a screen recording for a region that briefly returns to an older picture (a clip that reappears for one frame, a panel that flashes its previous state). It compares cropped, downscaled frames and reports a frame that differs between two matching frames within a window. Continuous motion is not flagged.

Why

A recording attached to a PR can look fine at a glance and still contain a one-frame reversion. Eyeballing does not catch it reliably; a mechanical frame comparison does, and every author can run the same check on their own captures.

How

  • Frames are cropped (--crop=W:H:X:Y), scaled to 96px wide, grayscale. A reversion is an A, B, A pattern: frame k nearly equals an earlier frame i while a frame between them clearly differs from both.
  • Exit codes: 0 clean, 1 flagged, 2 could not check (bad option, missing binary or file, undecodable video, time limit). A failure to check can never read as clean.
  • Each video has one wall-clock budget (--timeout, default 120s) shared by ffprobe, ffmpeg and the comparison. A timed-out tool is killed with its whole process group, also when the checker itself gets SIGINT or SIGTERM.
  • Options are validated up front; the frame height is passed to ffmpeg explicitly so the size the check assumes always matches what ffmpeg produces (a two-pixel-tall crop used to make the assumed frame size zero and the loop never end).
  • A short "Checking a capture" section in CONTRIBUTING.md.

Test plan

  • Unit tests added: 22 in scripts/check-capture-reversion.test.mjs (node --test), registered in test:scripts. They cover the thresholds, window boundary, ordering, option validation, the real CLI exit codes (0, 1, 2) including a symlinked path, the time limit against a fake hung ffmpeg and ffprobe, and no orphaned child after a timeout or SIGTERM.
  • Manual testing performed: run over eight fresh Studio drop recordings (clean on the timeline strip) and three older recordings (two flag the known reversion), each with --crop on the timeline strip.
  • Documentation updated

An independent adversarial review ran over four rounds; its findings (exit-code conflation, NaN options reading clean, a hang on a thin crop, a symlink entry-guard silent pass, deadline granularity, orphaned children) are fixed and covered by tests, and I mutation-checked the guards.

… picture

check-capture-reversion.mjs compares cropped, downscaled frames and
reports a frame that differs between two matching frames within a
window. Continuous motion is not flagged. Adds a unit test and a short
section in CONTRIBUTING.
Also replaces a stale comment on probeSize that contradicted the code.
A bad option, a missing binary or file, or an undecodable video used to
read as clean or crash with exit 1, which the caller cannot tell from a
flagged capture. Options are now validated, the frame height is passed
to ffmpeg explicitly so it always matches the size the check assumes,
and the tests pin the thresholds, window boundary, ordering and mean.
The entry-point guard compared argv[1] with a realpath, so a symlinked
path (macOS /tmp) skipped main and exited 0 with no output. It now
compares realpaths, checks every video before exiting with the most
severe code, and the tests drive the real CLI for exit 0, 1 and 2.
… spinning

A crop only two pixels tall made the assumed frame size zero, so the
comparison loop never ended. A non-positive frame size now throws, and
each video has a wall-clock limit (default 120s, --timeout) covering the
ffmpeg decode and the comparison; hitting it exits 2.
The limit is now one budget per video shared by ffprobe, ffmpeg and the
comparison, checked on every frame pair so a large --window cannot run
past it. A timed-out tool is killed with its whole process group, so a
shell-wrapper ffmpeg leaves no orphan. --timeout is capped below the
Node timer limit and inherited option names are rejected.
The tool runs in its own process group so a timeout can kill a wrapper
script and its children. That left the child running when the checker
itself got SIGINT or SIGTERM. Live groups are now killed on either
signal, and the checker exits 2.
@github-actions

Copy link
Copy Markdown

Fallow audit report

Found 2 findings.

Dependencies (1)
Severity Rule Location Description
minor fallow/unused-dev-dependency packages/cli/package.json:53 Package '@google/genai' is in devDependencies but never imported
Health (1)
Severity Rule Location Description
critical fallow/high-crap-score scripts/check-capture-reversion.mjs:124 'parseArgs' has CRAP score 156.0 (threshold: 30.0, cyclomatic 12)

Generated by fallow.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant