Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions native2/CUTOVER.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,11 @@ Found while porting desktop_dialog_answer's path entry, which now follows Peekab

### Runtime dylib

- The dev app signs it and the client with the team identity, and its signed peer check holds: the bundled client is served, and an unsigned one is dropped before its hello and refused with HOST_NOT_SERVING. The app's approval prompt and pause haven't been run end to end through it.
- The dev app signs it and the client with the team identity, and its signed peer check holds: the bundled client is served, and an unsigned one is dropped before its hello and refused with HOST_NOT_SERVING. Its SELF_TARGET guard holds live (#120's first check, 2026-10-20, through the dev app's bundled `desktop-tools-mcp`): desktop_windows lists both the dev app's and the installed app's windows, and desktop_activate on either is refused before dispatch. The approval prompt and pause haven't been run end to end through it, since only the user can answer the prompt or pause from the menu.

### Not yet checked live

- live-check's Safari checks (#79, #82, #86, #88, #92's web grid, #99's web field, #107's read) skip while the user's Safari is open. #90 switches input sources and #120 needs the app's approval prompt.
- Background launch and open ([#162](https://github.com/githubnext/desktop-tools/issues/162)) haven't run live. The check: type continuously in TextEdit while launching an app that isn't running and opening a PDF in Preview, both without `activate`. No keystroke should land elsewhere and TextEdit should stay frontmost; then inspect and click the new windows in the background. It needs the user's go-ahead, since a regression takes their focus.
- live-check's Safari checks (#79, #82, #86, #88, #92's web grid, #99's web field, #107's read) skip while the user's Safari is open. #90 switches input sources, and #120's second check needs the user to answer the app's approval prompt.

### Bench on real apps

Expand Down Expand Up @@ -91,8 +90,8 @@ Each step ships on its own and can be reverted.

1. **Keep it from rotting.** Done: CI builds and tests native2 and checks the contract. Port each tool change to native2 in the same PR, with its calls entries.
2. **Close the tool gaps** above. Run each fix through its calls and steps files.
3. **Finish the live checks** with the user's go-ahead, since they take over the pointer and the front app. Done: steps-pointer, steps-manage, the Dock step, and live-check #70, #77, #79, #81, #82, #86, #88, #89, #92, #99, #107, #110, #111 and #114 through `--server`, where Swift and Node now pass the same checks. Still to run: the Safari checks with Safari closed, and #162.
4. **Harden the runtime.** The socket lease (`<socket>.lock`, as Peekaboo's) and draining calls on stop are in. The dev app signs the dylib and client, and the peer check refuses an unsigned client. Still to do: sign the MCP binary once it's bundled (step 7), and run the approval prompt, pause and SELF_TARGET checks through the app.
3. **Finish the live checks** with the user's go-ahead, since they take over the pointer and the front app. Done: steps-pointer, steps-manage, the Dock step, and live-check #70, #77, #79, #81, #82, #86, #88, #89, #92, #99, #107, #110, #111 and #114 through `--server`, where Swift and Node now pass the same checks. Still to run: the Safari checks with Safari closed. Background launch and open ([#162](https://github.com/githubnext/desktop-tools/issues/162)) passed live on 2026-10-20 through the dev app's bundled `desktop-tools-mcp`, polling the frontmost app every ~30 ms: Ghostty, where the user types, stayed frontmost with focus in its text area while Calculator (not running) launched and a PDF opened in a running Preview, both without `activate`. Neither app became active. A background click entered 7 in Calculator, and the PDF window inspected and scrolled in the background.
4. **Harden the runtime.** The socket lease (`<socket>.lock`, as Peekaboo's) and draining calls on stop are in. The dev app signs the dylib and client, and the peer check refuses an unsigned client. Still to do: sign the MCP binary once it's bundled (step 7), and run the approval prompt and pause checks through the app. The SELF_TARGET check passes.
5. **Bridge the wire.** Done: native2's `desktop-tools-client`, one-shot (`<client> <socket> <tool> [--verbose]`, arguments on stdin, one JSON line out) or serving (`<client> <socket> --serve`, one request line in and one reply line out per call, tagged by id, with `{"cancel": id}` passed to the host), opening its containing app in the background when no host serves; and `createDesktop({ wire: 2 })`, which keeps one serving client per config, restarts it when it exits, and holds the event loop only while a call waits ([#172](https://github.com/githubnext/desktop-tools/issues/172)). Against one release host, `desktop_windows` p50 is 13.0 ms through `dist/mcp.js --wire 2`, 10.8 ms through native2's MCP server, and 16.5 ms for a one-shot client. `createDesktop`, `/protocol`, `/tools`, `dist/mcp.js` and ace keep working against a native2 host by changing one option.
6. **Dogfood in the app.** The switch is in: `npm run dev-app -- --native2` bundles native2's dylib and client in place of `native/`'s, and native2's client answers `desktop-client status` and `repair` with `native/`'s output. The bundled dylib is 4.3 MB and the client 0.3 MB, against 40 MB and 43 MB for `native/`'s. In the dev app, with its own grants, `live-check` through `dist/mcp.js --wire 2` passes the same 26 checks as the standalone host, #114's Finder files paste included. Remaining: run the dev app daily with `DESKTOP_TOOLS_STATE=~/.local/state/desktop-tools-dev node dist/mcp.js --wire 2`.
7. **Ship the Swift MCP server.** Bundled: `npm run dev-app -- --native2` puts `desktop-tools-mcp` in the app, signed with the client's identifier, since the host admits exactly one. Run from the app, it serves the app's own socket (`DESKTOP_TOOLS_STATE`, or the app's state folder), opens the app in the background when nothing serves it, as the client does, and refuses with HOST_NOT_SERVING when a signed host drops it. Through it, the dev app passes the 26 live checks the standalone host does, #114 included. The release builds native2's app, dylib, client and MCP server; run the release workflow with `stack: native` to ship `native/`'s again. Still to do: point the README's setup at the bundled server, keep `dist/mcp.js` as a shim that runs it, so existing `npx` configs keep working, and cut the release.
Expand Down
Loading