Developer harness
The harness is a developer loop for working on Handsy itself. From a Mac, it installs the iPhone app and updates the board’s firmware over Wi-Fi, starts a task on the phone, and brings back the run’s log and the steps it took. You don’t plug in the phone or the board along the way.
The harness is open source. It lives in harness/ on the release-1.0 branch; harness/README.md has the full setup. This page gives the overview. Questions: [email protected].
What it solves
Section titled “What it solves”- Slow reinstall cycles. Without it, each change means plugging the phone in to reinstall the app, unplugging the board to flash it, and plugging things back in to collect logs. The harness does all of this over Wi-Fi.
- Testing on a real phone. Handsy works by looking at a real iPhone screen and acting on it. Simulators can’t show that. The harness runs real tasks on a real phone with the board attached, the way a user would.
- Seeing what happened. Each run leaves a log and a step-by-step trace: what the app saw, what it decided, and what it did. The harness copies both back to the Mac so you can read them, or hand them to your coding agent.
How it fits together
Section titled “How it fits together”The Mac talks only to the iPhone, over Wi-Fi, using Xcode’s devicectl. The board stays plugged into the phone. Firmware goes Mac → app → board, the same way the app’s own board update works.
For this, the board joins the Mac’s Wi-Fi network instead of opening its own hotspot (station mode). The iPhone stays on the Mac’s network and reaches the board there too.
The scripts
Section titled “The scripts”| Script | What it does |
|---|---|
harness/app.sh |
Build the Debug app, install it on the test iPhone, and start it. |
harness/fw.sh |
Build the firmware, copy it into the app, and let the app install it on the board. |
harness/run.sh "<task>" |
Start a task in the app and print how it ended. |
harness/logs.sh [--runs] |
Copy the app’s debug.log to the Mac, and with --runs the latest run’s trace. |
harness/hid.sh "…" |
Send raw board commands through the app (keys, typing, pointer moves), plus a screenshot. |
harness/board.py |
One-time setup: put the board on the Mac’s Wi-Fi (net sta), or back on its hotspot (net ap). |
fw.sh also has checks for the update path: --corrupt sends a damaged image (the app refuses it and the board keeps running), and --crash-test sends an image that fails to start (the board goes back to the previous firmware by itself).
One-time setup
Section titled “One-time setup”-
iPhone. Turn on Developer Mode, pair the phone with the Mac in Xcode with Connect via network, and join the Mac’s Wi-Fi. Set
HARNESS_IPHONEto the phone’s name or ID. -
Lightning iPhones. Use a Lightning to USB 3 Camera Adapter with a charger in its Lightning port. See Lightning iPhone: camera adapter and power.
-
Board on the Mac’s Wi-Fi. Quit the app on the iPhone, replug the board, and within 3 minutes run:
Terminal window uv run harness/board.py net stamacOS asks to pair, then asks for the Wi-Fi name and password in a dialog. They go to the board over the encrypted link only. Open the app again.
-
Mirror again. On the iPhone, open Control Center → Screen Mirroring → ESP32 Agent.
Daily loop
Section titled “Daily loop”harness/app.sh # build, install, start the Debug appharness/fw.sh # build and update the board through the appharness/run.sh "Open Settings" # run a task, print how it endedharness/logs.sh --runs # copy the log and the latest run to the MacAfter each firmware update the board restarts, so choose ESP32 Agent in Screen Mirroring again.
Go back to the hotspot
Section titled “Go back to the hotspot”When you are done developing, put the board back on its own hotspot:
uv run harness/board.py net apThe board remembers station mode across restarts and updates until you switch it back. More: iPhone joins home Wi-Fi instead of the ESP32 Agent hotspot.
Safety
Section titled “Safety”- Debug builds only. The harness commands exist only in Debug builds of the app. The App Store and TestFlight apps ignore them and install signed firmware only.
- Updates are checked. Even in Debug builds, the app checks the image’s size and checksums before sending it. A damaged image is refused and the board keeps what it has.
- Pairing window. A new Bluetooth peer can pair only in the 3 minutes after the board powers on.
- Secrets stay out of logs. Wi-Fi passwords are typed into a macOS dialog, sent only over the encrypted link, and never logged.
- Trusted networks. In station mode the board relies on your network. Use it only on a trusted network, and switch back with
net apoutside development.
Logs and traces can include screenshots and task text. Treat them as private. For collecting logs without the harness, see Debugging & logs.
