Skip to content

feat: workspace-scoped device pinning (lock a project to one device) #2161

Closed

Description

@thymikee

Motivation

User feedback from a client project:

I had tons of issues with agent-device always trying to open iPhone simulator (default) when I really wanted the session to be scoped to a particular iPad only when I was working with real client's project. Solved it by wrapping every agent-device command with my custom bash script. I think simlock might be a good tool to enforce that.

Project config already provides overridable device defaults. A project also needs an opt-in constraint that prevents explicit selectors, nested commands, or selection heuristics from targeting a different device.

Accepted configuration

{
  "devicePin": {
    "platform": "ios",
    "device": "iPad Pro"
  }
}
  • devicePin is a non-empty object containing platform, device, udid, and/or serial. Boolean values are not supported.
  • Without devicePin, top-level selector defaults retain their existing overridable behavior.
  • A pin supplies its own selector defaults. Ordinary config and environment defaults cannot change its values.
  • Keep the pin and its source config file together. No provenance map is needed for unrelated flags.
  • A project pin replaces the entire user-config pin. Selector fields are not merged between pins. An explicit --config / AGENT_DEVICE_CONFIG file replaces both implicit config files.
  • Pinning is configured through these files. Do not add a --device-pin flag or pin environment variable.

Required behavior

  • Selector-less commands use the pin or fail if it is unavailable. They never fall back to an unpinned device.
  • Conflicting CLI selectors, MCP inputs, remote connection defaults, and batch steps fail with INVALID_ARGS and details.reason: "device_pin_conflict". The error identifies the pin, its config source, and how to edit or remove it.
  • Compatible refinements pass. Use the existing device-name and platform matching semantics. A supplied UDID or serial must resolve to a device that satisfies every pinned value.
  • Check the actual device before granting runtime access. Preserve the constraint through batch and replay child requests, including existing sessions and sessionless device operations.
  • Tool input cannot replace or disable operator-configured pinning.
  • Surface the active pin in devices / doctor so agents can discover the constraint.

Non-goals

Device ownership and cross-worktree exclusion belong to #1320. Holding a claim while a child process runs belongs to #3263. A workspace pin is a selection constraint, not a competing claim or provider lease.

Implementation

PR #4384 implements configuration and enforcement. Active-pin visibility in devices / doctor remains follow-up work before this issue is complete.

Activity

  1. thymikee commented on Aug 31, 2026

    @thymikee
    MemberAuthor

    Valid feature request, but not AFK-ready yet. Please decide the workspace/root/config authority (environment-only v1 versus project config), selector grammar and precedence, covered command families, daemon/proxy/provider semantics, and how the active pin is surfaced. Keep this distinct from #1320’s host-global device claims: a workspace preference may guide selection, but it must not become a competing ownership authority. Marking .

  2. thymikee commented on Aug 31, 2026

    @thymikee
    MemberAuthor

    Correction with exact wording: Valid feature request, but not AFK-ready yet. Please decide the workspace, root, and config authority; selector grammar and precedence; covered command families; daemon, proxy, and provider semantics; and how the active pin is surfaced. Keep this distinct from issue 1320 host-global device claims: a workspace preference may guide selection, but it must not become a competing ownership authority. Label applied: needs-triage.

  3. thymikee commented on Oct 9, 2026

    @thymikee
    MemberAuthor

    Decision: accepted as an explicit, opt-in pin policy, separate from #3263.

    agent-device.json already provides platform/device/udid/serial defaults (merged in cli-config.ts), and defaults must stay overridable: making them mandatory would change existing behavior. Scope:

    • A separate opt-in setting in project config that turns the selection into a pin.
    • Enforced at device selection: an explicit selector that contradicts the pin fails with a typed reason saying which source set the pin, instead of silently winning.
    • Keep track of where each value came from (pin, default, or flag) through the merge.

    #3263 (holding a claim while a child process runs) is a different feature and stays separate.

  4. thymikee commented on Nov 11, 2026

    @thymikee
    MemberAuthor

    Decision: device pin configuration: use an explicit selector object, not a Boolean that freezes ordinary defaults:

    {
      "devicePin": { "platform": "ios", "device": "iPad Pro" }
    }

    Keep the pin and the config file that supplied it together. A project pin replaces the entire user pin; --config / AGENT_DEVICE_CONFIG replaces both implicit config files. Ordinary defaults and environment variables do not change pinned values. Do not add a --device-pin flag or Boolean environment switch.

    Validate explicit selectors with the existing matching semantics and reject contradictions with INVALID_ARGS, details.reason: "device_pin_conflict", the source, and the pinned selector. Also validate the actual selected device before runtime access, so an otherwise unpinned UDID or serial cannot escape a name pin. CLI, MCP, batch, and replay must retain this constraint. General provenance for unrelated flags is not required.

    PR #4384 is being updated to this design. Showing the active pin in devices / doctor remains follow-up work on this issue.

  5. apex-sentinel-staging commented on Nov 11, 2026

    @apex-sentinel-staging

    Apex Sentinel · Issue review

    Suggested label: feature

    The issue requests a new opt-in devicePin configuration that constrains device selection across commands, MCP inputs, sessions, and tools, with detailed config schema and required failure behavior.

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions