Skip to content

Setup: provide approved Appium and Android emulator toolchain across CLI, Java, MCP, and engine #4898

Description

@MohabMohie

Objective

Implement the MOBILE_ANDROID setup profile promised by #4849 so CLI, Java, MCP, and engine runtime share one pinned Appium/Android owner. A reviewed plan must be able to install and verify Appium 3, the Inspector plugin, UiAutomator2, Android command-line tools, platform-tools, API platform, build-tools/aapt2, Emulator, one host-compatible system image, and a SHAFT-owned AVD; start/stop/logs must own the emulator and Appium server without hidden downloads or global environment mutation.

Parent: #4849

Current evidence (researched 2026-08-13)

The only current installer is package-private McpMobileToolchainService. It:

  • downloads unverified Node and Android command-line-tools archives;
  • runs mutable npm install, then Appium extension CLI installs;
  • scripts y into sdkmanager --licenses without binding accepted license IDs to the reviewed plan;
  • installs platform-tools, emulator, platforms;android-<api>, and a system image, but omits build-tools, so aapt2 is unavailable (the concrete Establish honest real-provider acceptance for Appium screen recording #4751 failure);
  • deletes/replaces extraction targets without shared setup receipts/rollback;
  • treats any adb ... device row as emulator readiness and never proves sys.boot_completed=1;
  • starts Appium with --relaxed-security by default and owns neither durable lease nor log identity;
  • is usable only through the MCP inspector-recording path. CLI/Java report MOBILE_ANDROID unsupported.

Current release pins are Node 24.19.0, Appium 3.6.0, Inspector plugin 2026.7.1, UiAutomator2 8.2.2, Android command-line tools 15859902, API 36, device pixel_8, tag google_apis, RAM 4096, cores 2. npm metadata confirms the three Appium packages support Node 24 and Appium 3.

Primary contracts:

Resolved design

Ownership and packaging

  1. Add AndroidSetupProvider, planner/manifest, installer, status verifier, and lifecycle owner to shaft-infrastructure; register it in InfrastructureSetupService.builtIn(). This is the only download/install/process owner.
  2. Refactor the already verified portable Node owner out of reporting into a shared internal service; Appium and reporting must use the same Node lock/destination and retain reporting plan compatibility.
  3. Package an exact Appium package.json/package-lock.json containing appium@3.6.0, appium-uiautomator2-driver@8.2.2, and appium-inspector-plugin@2026.7.1. Run npm ci --ignore-scripts --no-audit --no-fund in staging, verify exact versions and Appium driver/plugin lists, then atomically publish. Do not globally install npm packages and do not call mutable Appium extension installation after publication.
  4. Package a canonical Android release manifest binding command-line-tools URL/SHA-256/size by supported host, API 36, exact build-tools revision, platform-tools, emulator, platforms;android-36, and system-image package by architecture. sdkmanager is the upstream package installer, but the exact package set and command-line-tools bootstrap are plan/receipt bound. Stable channel only; --no_https, alternate repository OS, and implicit upgrades are forbidden.
  5. Use ShaftCachePaths: archives/npm cache in downloads/cache, versioned Appium and Android SDK under tools, AVD data/leases/logs under durable data. Reject relative roots, linked/reparse ancestors and non-regular executables before reads, writes, or execution.

Selection and public adapters

  1. Add an immutable AndroidSetupRequest rather than overloading SetupOptions. It binds AVD name, API, device profile, image tag, host-compatible ABI, RAM, cores, and server port. Defaults reproduce the release manifest. Normalize and validate all values before URI/path creation. Expose additive coordinator and SHAFT.Infrastructure overloads.
  2. Add CLI options on status|doctor|plan|install|verify|start: --api-level, --device-profile, --image-tag, --abi, --avd-name, --ram-mb, --cores, and --port. install reconstructs the request from the reviewed plan; repeated selectors are optional but must match. Persisted plan actions remain authoritative.
  3. Replace MCP install/start code with a compatibility adapter over the shared coordinator/lifecycle. Preserve existing tool responses and proposal confirmation semantics, but translate the confirmed proposal into the exact plan/approval/license flow. Delete the duplicate downloader/extractor/npm/sdkmanager owner after parity proof.
  4. Engine local Appium bootstrap uses the shared runtime only when setup mode is managed and a compatible receipt exists. An explicit non-local execution address always wins and triggers zero local setup. Child-only environment receives APPIUM_HOME, ANDROID_HOME, ANDROID_SDK_ROOT, ANDROID_AVD_HOME, and PATH additions; process-global environment remains untouched.

Licenses, integrity, offline, and privilege

  1. Bind the Android SDK license agreement identifier and all installed package license IDs into plan actions. CLI/Java/MCP must receive explicit matching acceptance before the first directory, process, or network action. Remove unconditional "y\\n".repeat(...) consent.
  2. EXTERNAL is read-only and executes no probes that can initialize caches. MANAGED/HYBRID installation is exact-plan only. offline=true succeeds only from a complete verified installed state or complete verified artifact/npm/sdk cache; otherwise it fails before directories/processes/network.
  3. Hypervisor, BIOS virtualization, Linux KVM group access, Windows Hypervisor Platform, macOS host entitlement, USB/device trust, and JDK availability are diagnostic prerequisites. Use emulator -accel-check; never enable features, elevate, edit shell profiles, or install host drivers.
  4. Appium binds only 127.0.0.1, does not enable --relaxed-security by default, has bounded/redacted logs, and is READY only after /status returns a compatible build. The emulator is READY only after its exact serial is online, getprop sys.boot_completed is 1, package manager responds, and the selected AVD/image identity matches.

Atomicity and lifecycle

  1. One normalized-root JVM lock plus OS file lock protects shared Node, Appium, Android SDK package install, AVD creation, receipts, and leases. Recheck under the lock. Separate JVM/process acceptance must converge.
  2. Archive extraction rejects absolute/traversal/drive/UNC/ADS paths, links/devices, duplicate/case-fold collisions, reserved Windows names, excessive entries/sizes/ratios, and linked ancestors. Downloads are size bounded and SHA-256 verified before extraction.
  3. Each install action is atomic. Known-good destinations are quarantined only after replacement is verified; rollback/recovery preserves the last usable copy. A partial failure exposes the failed action and partial receipt but writes no final profile receipt. Retrying reuses verified completed actions.
  4. start creates or reuses only SHAFT-owned leases. Start emulator first, wait for full boot, then start Appium and verify /status. On any later failure, tear down resources started by that call and retain logs. close/stop validates PID/start-instant/command/root/endpoint before terminating descendants then parent with one bounded deadline. Unknown ports/PIDs are never adopted or killed. logs reads only the lease-bound bounded files.

Caller matrix

Caller Mode/precedence Paths/environment Mutation authority Result
CLI explicit remote > requested mode > external default absolute SHAFT roots; child env only plan digest + exact license IDs JSON/text plan, receipt, lease/logs, exit 0/2/3/4/5
SHAFT.Infrastructure API > properties > defaults; explicit remote wins caller cwd irrelevant explicit SetupApproval; privileged work rejected plan/status/install/start/managed close
Driver bootstrap remote sessions never require local assets receipt-bound roots no hidden install; auto-start only with prior approval/receipt actionable remediation or owned endpoint
MCP recorder confirmed proposal translated to reviewed plan workspace never used for tools exact digest/license confirmation existing compatible response plus shared leases
Concurrent process receipt identity wins same user roots lock + recheck one publication, one compatible lease policy

State/failure matrix

State Preflight Mutation/process Readiness/result Recovery
External/system compatible read-only exact probes only none READY with external ownership user-managed
Missing, unapproved/license missing validate plan/approval first none MISSING/denied exact remediation
Managed online cold disk/host/license/network/manifest stage Node/Appium/SDK/AVD, verify, publish, receipt READY only on exact versions/packages retry reuses verified actions
Managed offline complete verify every required cache/install no network install/reuse succeeds stable
Managed offline incomplete/corrupt verify all before mutation/process none deterministic failure populate exact cache online
Partial install preserve completed verified actions no final profile receipt failed action + partial receipt retry exact plan
Healthy owned runtime lease + endpoint + boot/status refcount/reuse only READY close decrements/stops by policy
Stale/mismatched runtime validate full identity do not adopt/kill mismatch DEGRADED explicit recovery guidance
Missing acceleration/JDK/trust diagnostic probe no elevation/global change MISSING/DEGRADED user fixes host prerequisite

Acceptance-to-proof map

Requirement Positive proof Negative/mutation proof
Exact deterministic plan golden full actions, request, licenses, digests per host mutate order/version/source/hash/package/build-tools/AVD/port => approval/replan rejection before callbacks
aapt2 included installed build-tools exact revision; aapt2 version and APK badging flow pass remove build-tools action/package => unit + gated Android acceptance fail
Explicit licenses approved IDs allow install missing/wrong ID => zero roots/process/network/provider mutation
Integrity/extraction official command-tools checksum; canonical npm lock; safe staged publish bad hash, truncation, oversize, traversal/link/collision, rollback failure preserve known-good/no receipt
Offline complete verified cache/install succeeds cold/mixed/corrupt cache => zero process/network/write, including roots
Concurrency two services/JVMs converge three-waiter lock-map and overlapping file lock mutants fail
Exact status Node/Appium/driver/plugin/sdk packages/aapt2/AVD exact => READY fake, wrong version, partial, symlink, stale receipt => DEGRADED
Emulator boot selected AVD reaches device + boot property + package manager adb device without boot completion times out and cleans owned process
Appium lifecycle local-only server /status, lease, logs, refcount occupied port, PID reuse, mismatched command/root never adopted/killed; failure cleans tree
Adapter parity CLI/Java/MCP plans and reports match delete provider registration/options/compatibility delegation => boundary tests fail
Remote/default safety explicit remote and EXTERNAL run zero local setup mutant callback/directory/process fails no-mutation snapshots
CI/package adoption packaged CLI plan/install/verify and gated emulator session wrong classifier/provider/resource omission fails workflow/boundary checks

Sequential RED → GREEN delivery

  • RED provider registration and complete deterministic Android plan, including build-tools and accepted license IDs.
  • GREEN immutable manifests, request model, plan/status provider, and exact resource/hash tests.
  • RED/GREEN shared Node refactor with reporting plan/status/install compatibility.
  • RED/GREEN locked Appium npm install, exact probes, offline/integrity/rollback/concurrency.
  • RED/GREEN Android command-line-tools bootstrap, sdk package set, AVD creation, full readiness.
  • RED/GREEN lease-safe emulator/Appium lifecycle and bounded logs.
  • RED/GREEN CLI/Java selection and lifecycle parity.
  • RED/GREEN MCP compatibility migration; delete duplicate installer/downloader.
  • RED/GREEN engine managed bootstrap and explicit-remote zero-local behavior.
  • Add packaged CLI and real gated Android emulator acceptance, module/workflow omission guards, publication/reactor/docs validators.
  • Independent adversarial correctness/failure/blast-radius review; resolve every blocking mutation proof before merge.
  • Companion docs PR: setup commands, licenses, platform prerequisites, cache/offline/lifecycle/recovery, MCP migration, mobile examples.

Definition of done

MOBILE_ANDROID is no longer catalog-only. CLI, Java, MCP, and managed engine execution agree on one exact plan/report/receipt and one owned Appium+emulator lifecycle. A clean supported host can reach a real UiAutomator2 session with aapt2 present after explicit reviewed approval/licenses; external/default/remote/offline-denial paths perform zero hidden mutation; no duplicate installer remains in MCP; every acceptance above has mutation-sensitive automated proof and all required CI/publication/documentation gates are green.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions