Files
T
q792602257andClaude Opus 4.6 bead6e58ac
Tests / Test failed: 2, passed: 691
chore(openspec): archive host-agent-dependency-supervisor
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>
2026-07-14 10:07:07 +08:00

48 lines
5.9 KiB
Markdown

## 1. Config
- [x] 1.1 Add `dependency_supervisor_enabled`, `appium_supervised`, `appium_host`, `appium_port`, `runtime_supervised`, `runtime_host`, `runtime_port`, `dependency_restart_max_attempts` fields to `HostAgentConfig` (`apps/device-host-agent/host_agent/config.py`), reading from `HOST_AGENT_DEPENDENCY_SUPERVISOR_ENABLED` / `HOST_AGENT_APPIUM_SUPERVISED` / `HOST_AGENT_APPIUM_HOST` / `HOST_AGENT_APPIUM_PORT` / `HOST_AGENT_RUNTIME_SUPERVISED` / `HOST_AGENT_RUNTIME_HOST` / `HOST_AGENT_RUNTIME_PORT` / `HOST_AGENT_DEPENDENCY_RESTART_MAX_ATTEMPTS`, all defaulting per design.md Decision 6.
- [x] 1.2 Unit tests for the new config fields' defaults and env var parsing, following the existing pattern used for `console_*` fields in the config test module.
## 2. Health probes and adoption
- [x] 2.1 Implement a small health-check helper per dependency: TCP connect + Appium `GET /status` check, and TCP connect + Runtime API health endpoint check (reuse an existing HTTP client already available to the Host Agent rather than adding a new dependency).
- [x] 2.2 Implement the adopt-vs-spawn decision: probe before spawn, log and mark "adopted" on a passing health check, log a port-conflict error and skip on a listening-but-unhealthy port, proceed to spawn on no listener.
- [x] 2.3 Unit tests: adopt when healthy instance present, conflict-and-skip when unhealthy instance present, proceeds to spawn when nothing listening (mock the TCP/HTTP probe).
## 3. Process supervisor core
- [x] 3.1 Create `apps/device-host-agent/host_agent/dependency_supervisor.py` with a class managing zero or more supervised dependencies (Appium, Runtime), each described by: command, host/port, health-check callable, adopted-vs-spawned state.
- [x] 3.2 Implement spawn via `subprocess.Popen` for Appium (`appium --address <host> --port <port>`) and Runtime (`uvicorn api.rest:create_app --factory --host <host> --port <port>`), capturing stdout/stderr and forwarding to Host Agent logging tagged by dependency name.
- [x] 3.3 Implement post-spawn readiness wait: poll the health check until it passes or a startup timeout elapses, logging failure distinctly from a later crash.
- [x] 3.4 Implement crash-detection + capped exponential backoff restart loop (only for spawned, not adopted, processes), stopping permanently per dependency once `dependency_restart_max_attempts` is reached, per design.md Decision 4.
- [x] 3.5 Implement graceful stop: terminate only spawned child processes on supervisor shutdown; adopted processes are left untouched.
- [x] 3.6 Unit tests: spawn success, spawn failure (missing executable), crash-triggers-restart-with-backoff, restart-exhaustion-gives-up, adopted-process-never-restarted-or-killed, stop-terminates-only-spawned-children.
## 4. Wiring into HostAgentApplication
- [x] 4.1 In `apps/device-host-agent/host_agent/app.py`, construct and start the dependency supervisor (if `dependency_supervisor_enabled`) before the heartbeat loop's first `connect_devices()` pass.
- [x] 4.2 Stop the supervisor during `HostAgentApplication` shutdown/teardown alongside existing heartbeat/console teardown.
- [x] 4.3 Integration test covering: supervisor enabled with both dependencies off (no-op, unchanged existing behavior), supervisor enabled with only Appium supervised, start/stop ordering relative to heartbeat loop.
## 5. Documentation
- [x] 5.1 Update `docs/MACOS_IPHONE_SETUP.md` to document the new opt-in supervised mode (env vars, defaults, adopt-vs-spawn behavior, restart/backoff behavior) as an alternative to the existing manual multi-terminal flow, without removing the manual instructions.
- [x] 5.2 Update `.env.example` (if present) with the new `HOST_AGENT_*` variables, defaulted to off/disabled, matching existing `.env.example` conventions for other opt-in Host Agent features.
> Note: the committed `.env.example` is scoped exclusively to `compose.deploy.yaml` Cloud-side variables (enforced by `tests/test_deployment_config.py::test_example_environment_contains_no_static_credentials`), and no other `HOST_AGENT_*` variables are listed there. Documenting the new variables in `docs/MACOS_IPHONE_SETUP.md` §9 instead matches the existing convention used for all other Host Agent opt-in features (e.g. `HOST_AGENT_CONSOLE_*`, `AI_PLANNER_*`), so no `.env.example` change was made.
## 6. Validation
- [x] 6.1 Run full non-integration test suite (`uv run --all-packages pytest -m "not integration"`) and confirm no regressions.
Result: `503 passed, 2 failed, 44 deselected`. Both failures (`tests/test_deployment_config.py::test_deploy_compose_has_only_cloud_services_and_minimal_environment` and `::test_example_environment_contains_no_static_credentials`) are **pre-existing**, caused by unrelated commit `03c7c30 LLM_PROVIDER_ENC_KEY` (in-flight `database-llm-provider-management` work that added `CLOUD_LLM_PROVIDER_ENCRYPTION_KEY` to `.env.example` and `compose.deploy.yaml` without updating this test's allowlist). Verified by stashing this change's working tree and re-running: same 2 failures remain on baseline. The new `tests/test_dependency_supervisor.py` (19 tests) and updated `tests/test_config.py` / `tests/test_app.py` (38 combined) all pass.
- [x] 6.2 Ruff check/format and `compileall` on changed files.
`ruff check` and `ruff format --check` clean on `apps/device-host-agent/host_agent/{config,dependency_supervisor,app}.py`, `apps/device-host-agent/tests/{test_config,test_dependency_supervisor,test_app}.py`. `python -m compileall -q` clean on the same set.
- [x] 6.3 `openspec validate --strict` for this change.
Result: `Change 'host-agent-dependency-supervisor' is valid`.
- [x] 6.4 Manual verification on macOS: start Host Agent with supervisor enabled and no Appium/Runtime running, confirm both are spawned and device reaches `idle`; then start Host Agent again with an already-running Appium, confirm it's adopted (not duplicated) and logged as such.
Verified by the user on a macOS host following `docs/MACOS_IPHONE_SETUP.md` §9.