Skip to content

capture: add batches, image comparison, and capture controls - #10

Open
aktanazat wants to merge 8 commits into
brijr:mainfrom
aktanazat:agent-capture-workflows
Open

aktanazat wants to merge 8 commits into
brijr:mainfrom
aktanazat:agent-capture-workflows

Conversation

@aktanazat

@aktanazat aktanazat commented Sep 22, 2026 •

Copy link
Copy Markdown

this now builds on the full-page viewport fix (#11), so that one should go first. what's left here:

  • capture_batch returns images in input order and keeps successful captures when another item fails. queued captures count toward their deadlines, and cancelling stops pending work.
  • compare checks saved images locally and can write a difference image. it handles transparency and different image sizes.
  • capture reports include readiness warnings and stage timings.
  • pixel budgets lower image density without changing the page layout. --quality and --freeze-animations round out the capture controls.
iris --full --max-pixels 1500000 --freeze-animations --json localhost:3000
iris --format webp --quality 75 -o card.webp --selector '#card' localhost:3000
iris compare before.png after.png --threshold 8 -o diff.png
mcp capture time before after
warm median 1,057 ms 611 ms
warm p95, nearest rank 1,261 ms 708 ms
  • same local element, 20 warm captures per version, macos 26.6.2 arm64, chrome 152.0.7977.83. run order: baseline, candidate, candidate, baseline. settings and raw samples. first-capture times varied; no startup improvement is claimed.

Keep full-page captures at the requested viewport so viewport-relative
layouts retain their proportions. Expose capture readiness and timing,
image quality, a pixel budget, and an animation-freezing option.

Share bounded capture capacity across MCP batches and image comparison.
Include queued work in capture deadlines and stop cancelled requests.
Compare saved images locally, with an optional difference image.
Keep concurrent captures in separate headless windows so a hidden tab
does not stall rendered-frame waits on macOS.

Run browser-backed unit tests without competing Chrome launches. Image
checks use the normal capture budget; timeout checks retain short budgets.

On one macOS runner, twenty warm captures per version took 1,057 ms
before and 611 ms after at the median. First-capture times varied.

Signed-off-by: Aktan Azat <aktanaazat@gmail.com>
Assisted-by: Claude Opus 5
Assisted-by: GPT-6 Astra
Assisted-by: Claude Sonnet 5
Keep the Rust rule pack in the repository so local edits use the same
style checks.

Signed-off-by: Aktan Azat <aktanaazat@gmail.com>
Assisted-by: GPT-6 Astra
Full-page captures resized the viewport to the document height before
taking the screenshot, so a 100vh section grew to fill the whole image
and the rest of the page fell off the bottom. Chromiumoxide's full_page
option, used for pages past the Retina limit, resizes it the same way.

Clip the whole document with captureBeyondViewport instead. The clip
keeps the device scale factor, so full pages stay at 2x up to the
existing ~16k px limit and fall back to 1x past it. Settle every image
in the document before the screenshot, which the resized viewport used
to do implicitly.

Assisted-by: Claude Opus 5.5
@aktanazat
aktanazat force-pushed the agent-capture-workflows branch from cf6c1a6 to 87867fe Compare September 27, 2026 09:32
Expose capture readiness and timing, image quality, a pixel budget, and
an animation-freezing option.

Share bounded capture capacity across MCP batches and image comparison.
Include queued work in capture deadlines and stop cancelled requests.
Compare saved images locally, with an optional difference image.
Keep concurrent captures in separate headless windows so a hidden tab
does not stall rendered-frame waits on macOS.

Run browser-backed unit tests without competing Chrome launches. Image
checks use the normal capture budget; timeout checks retain short budgets.

On one macOS runner, twenty warm captures per version took 1,057 ms
before and 611 ms after at the median. First-capture times varied.

Signed-off-by: Aktan Azat <aktanaazat@gmail.com>
Assisted-by: Claude Opus 5
Assisted-by: GPT-6 Astra
Assisted-by: Claude Sonnet 5
Assisted-by: Claude Opus 5.5
@aktanazat
aktanazat force-pushed the agent-capture-workflows branch from 87867fe to b0bf90f Compare September 27, 2026 09:37
@aktanazat

Copy link
Copy Markdown
Author

split the full-page viewport fix out into #11 and dropped the lint rules and the jev readme section, those were my own tooling. happy to split compare and capture_batch into separate prs once #8 lands.

Iris looked only for Chrome, Chromium, Edge, and Brave, so on a Mac with only Helium every capture failed with chromiumoxide's 'Could not auto detect a chrome executable'. Helium is now a default candidate, and the detection and launch errors say to set CHROME or pass --chrome.

Assisted-by: Claude Opus 5.5
Helium leaves its network service helper running after the browser exits, reparented to launchd, so every capture and every MCP shutdown leaked one process (one lived over two minutes) and the SIGTERM cleanup test failed. Closing a session now stops every process still started with the session's profile directory, with SIGTERM and then SIGKILL, matching the profile argument exactly.

Assisted-by: Claude Opus 5.5
The fork holds the later rewrite of the capture commit (b0bf90f), rebased
onto the full-page viewport fix (ce73729). Local work on top of the
original capture commit: the Rust anti-slop rules, Helium detection, and
reaping browser helpers.

Conflicts in README.md and src/capture.rs resolve to the fork's rewrite.
ce73729 carries the full-page behavior, the ~16k px fallback to 1x, and
its two tests. The rewrite dropped the full-page before/after section and
the optional Jev section, so the two images only that section showed are
removed. The Helium, CHROME, and helper-reaping changes merge cleanly.

Assisted-by: Claude Opus 5.5
The anti-slop unwrap rule reads a SAFETY note only directly above the statement holding the expect. The note sat inside a closure bound by a let, so the let was flagged. Mapping the three channels puts the note directly above the expect, with the same arithmetic.

Assisted-by: Claude Opus 5.5
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