Skip to content

macOS: let native-backend app sessions claim one app instead of the whole Mac #3322

Description

@janicduplessis

Use case

Several coding agents run on one Mac, each building and testing its own macOS app (each with its own bundle id) through the native backend (AGENT_DEVICE_MACOS_APP_BACKEND=native, ADR 0031). Each agent has its own daemon (separate AGENT_DEVICE_STATE_DIR). Today only one of them can run: every macOS session takes the single device claim local:apple:macos:host-macos-local, so the others get DEVICE_IN_USE even though a native app session only touches its own app through accessibility.

Related: #3254 (no activation, no real pointer), #3229 and #3236 (the macos-app lease, which takes no claim), #3213 (native backend follow-ups). I found nothing open for local concurrent macOS sessions.

Proposed behavior

Native-backend sessions on the app surface whose bundle id is known before launch claim <host key>:app:<bundleId> instead of the host key:

  • Two sessions on different bundle ids coexist; two on the same bundle id conflict (DEVICE_IN_USE).
  • A whole-Mac claim (XCTest backend, desktop/menubar/frontmost-app surfaces, deep-link-only opens) and any foreign per-app claim exclude each other. Lock order is host key, then app key.
  • The per-app record is a schema-v2 claim with an optional app: { bundleId }, so sweep, device release --stale, takeover and idle expiry reuse the existing paths.
  • Native open uses open -g, so opening an app no longer activates it.
  • screenshot takes a short host-wide capture lock (see below).
  • macos-app leases are unchanged.

Evidence (macOS 27.0, native backend, two fixture AppKit apps, two daemons)

  • Both sessions ran open, snapshot, click, fill, type, screenshot concurrently. The frontmost app was unchanged after every step, and both apps logged active=false at launch.
  • Per-app claim files were created for each bundle id. A third session on the same app, and a whole-Mac desktop session, both got DEVICE_IN_USE (DEVICE_CLAIM_LIVE_OWNER).
  • Without per-app claims, the second daemon is refused with DEVICE_IN_USE.
  • Two concurrent ScreenCaptureKit captures failed (screenshot failed) or hung to the 30 s helper timeout in four of four attempts; a single capture takes about 0.5 s. Hence the capture lock. Other actions run in parallel.

Open questions for maintainers

  • Is per-app scope acceptable at the claim layer, or should it live elsewhere?
  • Mixed versions on one claims dir: an older daemon reads a per-app record as inconsistent and does not scan per-app files when taking the host key, so versions do not exclude each other (same boundary as ADR 0030).
  • devices shows claimedBy only for the host key; device status lists per-app claims.

I have a branch with the implementation and ADR 0034 and will open it as a draft PR.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions