3ep.dev

Agent-driven QA for Mac apps, 39x faster than screenshots and clicks.

A computer-use agent takes 45 seconds for a ten-step journey, takes over your screen to do it, and one run in three reported success on a wrong result. offstage runs the same journey in 1.15 seconds behind your other windows, and verifies against what the app actually saved.

macOS 15+. Python CLI plus Swift probes. MIT.

/screens

ONE BATCH CALL, 25 STEPS, RECORDED IN THE BACKGROUND
  • The NotesApp fixture window. Five notes appear in the sidebar one after another, get renamed to Groceries, Ideas, Trip to Lisbon, Reading list and Call mom, get reordered twice, Groceries is deleted through the File menu, Call mom is marked done and moved to the bottom, and a new Dentist note lands at the top. The window is captured through ScreenCaptureKit while it sits behind other windows.

    Five notes added, renamed, reordered, one deleted, nine state checks. 1.5 s without the pauses.

/idea

FOUR CHANNELS, ONE REFUSAL

A screenshot agent needs the foreground. The app comes to the front, the model starts clicking, and you go make coffee. Every observation costs an image, and the only thing it can check is whether the window looks right.

offstage reads the accessibility tree instead of pixels, presses buttons and menus through the accessibility API, and talks to a small port the app embeds in debug builds for typing and drags. Verification reads the app’s persisted state, so a bug shows up as a mismatch instead of a screenshot that looks fine.

The one thing it refuses to do is send synthetic keyboard or mouse events. Those land in whatever window is frontmost, and that window is usually yours.

/measured

4 EXAMPLES

The same journey, two notes, two renames and a menu delete, run three ways. Full tables live in the repo under docs/benchmarks.md.

  • Screenshot-driven agent44.7 s, took the screen, 1 of 3 runs wrong
  • XCUITest54.5 s, took the screen
  • offstage batch1.15 s, in the background
  • Seeded defects caught by scripted batteries21 of 21, 0 tokens