# Screen Recording — how capture works & the stale-grant fix

Jarvis sees the screen via `snap.sh` → control/screenshot.json → the ENGINE
process captures with `screencapture` → control/screenshot.done → Jarvis reads
the PNG. Capture MUST happen in the engine (child of Jarvis.app, the granted
identity), NOT via a `screencapture` command in a tool call — from the bundled
claude CLI's process tree macOS attributes the request to the wrong process and
denies it. That was the original "no matter how many times I grant it, denied"
bug. (voice/screen.py, snap.sh, control.py screenshot handler.)

**If capture says "no permission" even though Jarvis.app is toggled ON:** the
TCC record is STALE (keyed to an old code identity from an ad-hoc→cert rebuild).
Toggling it off/on does nothing. Fix, once:
1. `tccutil reset ScreenCapture com.ahmed.jarvis`
2. Re-grant: Screen Recording pane → toggle Jarvis on (or "+" add
   ~/Desktop/Jarvis.app if it's not listed).
3. FULLY quit + relaunch Jarvis (a self-restart/execv is not enough for a
   screen grant to take effect).
It sticks after that because the app is signed with the persistent cert
(identity com.ahmed.jarvis is stable across rebuilds). main.py requests access
on startup, so a relaunch self-heals the prompt. Same signature-stale trap can
hit Accessibility — see [[accessibility-permission]].
