Implementation verified on macOS (task 6.4 confirmed spawn + adoption behavior). Archives the change under openspec/changes/archive/2026-07-14-host-agent-dependency-supervisor/ and syncs the delta spec into a new main capability at openspec/specs/host-agent-dependency-supervisor/spec.md (5 baseline requirements covering opt-in default, adopt-don't-fight, spawn, bounded backoff restart, and lifecycle tied to Host Agent). openspec validate --strict passes on the synced spec. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
4.7 KiB
Why
On the macOS native real-device setup (docs/MACOS_IPHONE_SETUP.md), a device only reports idle/online to the Cloud Control Plane if DeviceManager.connect() (device/manager.py:68-108) can actually establish an Appium session — but the Host Agent process (apps/device-host-agent/host_agent/app.py) never starts Appium itself; the operator must run appium --address 127.0.0.1 --port 4723 in a separate terminal before starting device-host-agent, and separately run the local Runtime API (uvicorn api.rest:create_app --factory ...) if they want to inspect device/task state during local debugging. Forgetting (or losing) either process is invisible from the Host Agent's own output: heartbeats keep succeeding, so the Host looks "connected," while the device silently stays offline and every assigned task stays queued forever with no error. This was diagnosed live from exactly that symptom. Having the Host Agent supervise these known local dependencies for the single-machine dev/real-device workflow removes an easy-to-miss manual step and makes the failure visible instead of silent.
What Changes
HostAgentApplicationgains an optional local dependency supervisor that, when enabled, starts and monitors two external processes alongside the existing heartbeat/claim loop:- Appium server (
appium --address <host> --port <port>), whose reachability already gates real deviceconnect()calls. - Local Runtime API (
uvicorn api.rest:create_app --factory --host <host> --port <port>), used for local inspection/debugging of device and task state.
- Appium server (
- Before spawning either process, the supervisor probes the configured address: if something is already listening and answers the expected health check, it adopts the existing instance (log-only) instead of spawning a duplicate or rebinding the port. It only spawns a subprocess when the port is free.
- Crash handling: if a supervised subprocess it spawned exits unexpectedly, the supervisor restarts it with capped exponential backoff and a maximum retry count per run; it does not touch a process it did not spawn (an adopted, externally-managed instance is never restarted or killed by the Host Agent).
- New
HostAgentConfigfields to control this, all opt-in and disabled by default (see design.md for the specific default-off rationale): enable/disable the supervisor as a whole, enable/disable each of the two dependencies independently, and host/port for each (defaulting to the existing documented conventions — Appium127.0.0.1:4723, Runtime127.0.0.1:8000). - Documentation update to
docs/MACOS_IPHONE_SETUP.mddescribing the new opt-in supervised mode as an alternative to the existing manual multi-terminal flow (the manual flow remains fully supported and is still what's documented as the default path). - Explicitly out of scope: no change to WDA's own lifecycle (Appium continues to own WDA session management entirely unchanged), no Docker Compose changes (
compose.yaml/compose.deploy.yamlare unaffected — this is a native-process-only capability), no change to the outbound cloud protocol.
Capabilities
New Capabilities
host-agent-dependency-supervisor: optional, opt-in local process supervision inside the Host Agent for the Appium server and the local Runtime API — port/adoption probing, spawn, health-checked readiness, and rate-limited crash-restart, scoped to the macOS native single-machine real-device workflow.
Modified Capabilities
(none — host-agent-protocol outbound behavior is unchanged; driver-registry/device-pool behavior for how a Driver.connect() reaches Appium is unchanged, this change only affects whether Appium happens to already be running when that connect attempt occurs)
Impact
- Affected code:
apps/device-host-agent/host_agent/(new supervisor module,app.pywiring to start/stop it alongside the heartbeat/claim loop,config.pynew fields),apps/device-host-agent/pyproject.toml(no new runtime dependency expected — spawning uses stdlibsubprocess; Runtime API is already invoked via its existinguvicorn/api.restentry point). - Not affected:
cloud.*,apps/cloud-api,compose.yaml,compose.deploy.yaml,driver/wda_driver.py/driver/android_driver.pyconnection logic, WDA's own session lifecycle (still fully owned by Appium). - Operational impact: opt-in only — a Host Agent with the supervisor disabled (the default) behaves exactly as it does today. When enabled, the Host Agent's own process lifecycle now indirectly affects two more local processes, which changes what "the Host Agent crashed" or "the Host Agent's log" means for local troubleshooting (see design.md for how adoption-vs-spawn is surfaced in logs to keep this legible).