# Read-only proof audit PASS 1: Android SDK desktop environment

PR: [#11648](https://git.cafe/pingdotgg/t3code/pulls/11648), [GitHub #11648](https://github.com/pingdotgg/t3code/pull/11648).
Workspace inspected: /home/ubuntu/wt/pr-11648, branch takeover/pr-11648.
Before: origin/main 843f4e3529. After: b6c944c9b8e36e9a7faddfafaf291677a5663eb9.
Original contribution: thelonewander3r/t3code fix/desktop-android-sdk-env c4c9ff1ee99dd41d53ade45bf47b8295d449de27 (identity supplied by requester, not independently fetched).
This is a matrix adequacy verdict, not a claim of executed runtime evidence or merge readiness. No app, browser, server, or tests were run. Concurrent test edits were observed and left untouched.

## Derived behavior

The production diff only adds two names to POSIX shell capture and hydrates them as one precedence group. If either inherited value is nonblank after trim, neither variable is changed. Otherwise each nonblank shell value is independently trimmed and installed. Missing or blank shell values do not delete existing blank inherited values. Neither name is inferred from its partner. Windows profile capture and installation are unchanged.

Desktop startup installs this environment before bootstrap. The local device host selects trimmed ANDROID_HOME first, then trimmed ANDROID_SDK_ROOT; otherwise it searches default locations and PATH. It checks platform-tools/adb, emulator/emulator, and cmdline-tools/latest/bin/avdmanager. Therefore an inherited ROOT must block hydration of shell HOME, even though HOME itself is absent.

## Symbols and isolation

H = ANDROID_HOME; R = ANDROID_SDK_ROOT; U = absent key; E = empty string; W = whitespace-only string; h/r = distinct shell-only SDK fixture paths; i/j = distinct inherited SDK paths. All paths are outside default SDK locations. Tuples below are (H,R) in the directly spawned backend at exec, before LocalDeviceHost normalizes its hub child environment.

Use distinct valid SDK fixtures for h,r,i,j so selection can be observed, and no SDK executable directory on PATH. For UI discovery-only proof a minimal synthetic SDK can be used, but label it synthetic and do not claim emulator launch success. A real SDK/AVD is needed to claim actual device launch. Preserve enough ordinary executable PATH for the shell probe and runtime. Keep an explicit PATH in profile output: the existing candidate loop stops at the first captured nonempty PATH.

## Fixture matrix

All POSIX rows apply to Linux and macOS semantics. Real before/after Linux Electron is required for affected behavior; macOS execution is not required because there is no darwin-only production diff. Expected values are source-derived predictions pending evidence.

| ID | Inherited (H,R) | Shell exports (H,R) | Before backend | After backend | Classification / downstream expectation |
|---|---|---|---|---|---|
| A1 | U,U | h,r | U,U | h,r | Affected; missing SDK -> selects h |
| A2 | U,U | h,U | U,U | h,U | Affected; missing SDK -> selects h; R stays absent |
| A3 | U,U | U,r | U,U | U,r | Affected; missing SDK -> selects r; H stays absent |
| C1 | i,j | h,r | i,j | i,j | Control; inherited pair wins; selects i |
| C2 | i,U | h,r | i,U | i,U | Control; no filling R; selects i |
| C3 | U,j | h,r | U,j | U,j | Critical control; no shell HOME shadowing inherited ROOT; selects j |
| C4 | U,j | h,U | U,j | U,j | Critical cross-key control; ROOT-only inherited vs HOME-only shell |
| C5 | i,U | U,r | i,U | i,U | Cross-key control; HOME-only inherited blocks ROOT-only shell |
| C6 | U,U | U,U | U,U | U,U | Control; missing SDK on both revisions |
| A4 | E,E | h,r | E,E | h,r | Affected; empty inherited values allow hydration |
| A5 | W,W | h,r | W,W | h,r | Affected; whitespace inherited values allow hydration |
| A6 | E,U | U,r | E,U | E,r | Affected; empty H preserved, ROOT selected |
| A7 | U,E | h,U | U,E | h,E | Affected; empty R preserved, HOME selected |
| C7 | E,j | h,r | E,j | E,j | Control; nonblank inherited ROOT blocks hydration despite empty HOME |
| C8 | i,E | h,r | i,E | i,E | Control; inherited HOME blocks hydration despite empty ROOT |
| C9 | U,U | E,W | U,U | U,U | Control; blank shell exports do not create keys |
| C10 | E,W | E,W | E,W | E,W | Control; blank inherited values are not deleted |
| A8 | U,U | E,r | U,U | U,r | Affected; shell blank HOME ignored independently |
| A9 | U,U | h,W | U,U | h,U | Affected; shell blank ROOT ignored independently |
| A10 | U,U | paths with internal spaces for h,r plus surrounding whitespace | U,U | trimmed h,trimmed r | Affected; internal spaces preserved, edge whitespace trimmed; selects h |
| A11 | U,U | h,r plus ordinary stdout banner and stderr noise | U,U | h,r | Affected; marker extraction excludes noise; no banner in either value |
| C11 | space-padded i,U | h,r | original padded i,U | original padded i,U | Control; inherited nonblank value preserved byte-for-byte, SDK consumer trims |
| W1 | U,U | Windows PowerShell profile h,r | U,U | U,U | Windows control; Android names are not captured there |
| W2 | i,j | Windows PowerShell profile h,r | i,j | i,j | Windows control; inherited SDK values untouched |

## Shell-selection control matrix (unchanged helper and loop)

| ID | Setup | Expected both revisions | Evidence level |
|---|---|---|---|
| S1 | Linux SHELL=/bin/zsh, user shell=/bin/bash | zsh first, bash next, Linux bash fallback deduplicated; each attempted shell gets -ilc | Source/focused mocked-spawner test; optional real alternate installed shell |
| S2 | Darwin same inputs | zsh first, bash next, Darwin zsh fallback deduplicated | Source/focused test; no macOS runtime requirement |
| S3 | SHELL missing/blank, user shell missing | Linux /bin/bash; Darwin /bin/zsh | Source/helper test with explicit userShell argument |
| S4 | explicit shell fails or yields no PATH | Try next distinct candidate; merge captures; stop when accumulated PATH is nonempty | Focused spawner test; failure behavior unchanged |
| S5 | first shell returns PATH but no Android exports; later shell has h/r | Later shell is not probed; SDK remains unhydrated | Unchanged control, not a promise to search every profile |
| S6 | Darwin probes have no PATH | launchctl getenv PATH may supply PATH only; it does not supply H/R | Source control; unchanged darwin-only fallback |

The real host's account shell can precede fallback even with throwaway HOME; env -i alone does not erase the passwd user shell. Record SHELL and actual probe executable/arguments. A successful PATH-only early candidate must not accidentally bypass the intended profile fixture.

## Actual app drive and robust spawned-process observation

1. Evidence agent builds real before and after Electron desktop artifacts with revision labels. Do not substitute a Node-only invocation of the service for this integration proof. Use Linux under headless-desktop. Maintain separate throwaway HOME and isolated T3 state per revision/row beneath /tmp/ev-11648; disable dev seeding. Do not reuse the live installation, credentials, browser profile, or state. No VITE_HTTP_URL/VITE_WS_URL overrides.
2. Launch Electron by absolute artifact path with env -i and an explicit small GUI-like allowlist (HOME, SHELL, ordinary runtime PATH, DISPLAY and required headless desktop session variables, isolated T3 home and row-specific inherited H/R). For U omit the assignment; for E pass an explicit empty assignment. Do not launch the artifact through a login shell. Record the exact nonsecret allowlist and fixture profiles. Verify the selected shell actually reads that throwaway HOME: some bash login setups reset HOME via system profile; choose a working installed shell/profile combination and record it rather than assuming ~/.profile was sourced. Profiles should export fixture values, not add SDK tools to PATH.
3. Record Electron PID at spawn. Identify its own backend descendant by parent chain/exec events and artifact/cwd, not name-matching unrelated processes. Read only H/R from /proc/<backend-pid>/environ (NUL-delimited) immediately after backend exec, or capture these two keys at that exec boundary with a process observer. This observes inherited exec environment before a shell can load a profile. Do not dump the full environment. Linux /proc of the long-lived Electron parent is NOT a reliable view of JS process.env mutations; inspect the newly exec'd backend. Retain launch manifest, PID ancestry and artifact identity to prove attribution. Backend changes/restarts require recapture.
4. In the real desktop client, open a local project/thread, use the right-panel add menu -> Device; complete/enable Device setup if prompted, then refresh the local device list. Settings device configuration is an alternate entry path. Ensure the selected host/environment is local, with no remote/EAS host masking local failure. Capture local Android platform availability/reason and selected root evidence. Before A1/A2/A3: Android SDK not found. After: SDK passes platform availability when all required binaries exist; a fixture without an AVD may correctly show 'SDK installed, but no virtual device exists'. Do not require a launched emulator for a hydration-only proof.
5. For strongest downstream root proof, use fixture adb/emulator executable sentinels that record only their SDK label and H/R when invoked by the real local-device discovery path; valid executable fixtures must implement the actual discovery commands used by that build. Alternatively observe the device-host child exec/argv and filtered environment. LocalDeviceHost rewrites its hub child's HOME SDK variable to the selected root, so that child's H/R cannot establish original backend tuple alone. Pair it with step 3. Distinct root labels make C3/C4 precedence visible.
6. A normal terminal printenv is not acceptable proof: its shell can source the same profile on both builds and mask the missing desktop hydration. If a terminal is used as supplemental evidence, run a true no-profile shell and prove its launch ancestry, but keep direct backend exec observation as primary. Record stdout/stderr profile noise separately from values.
7. Run each affected row against both artifacts and controls against both when feasible; at minimum A1/A2/A3, an empty-value affected row, spaces/noise, and C3/C4 must have real Linux Electron observation. Remaining edge/control permutations may use focused service tests, explicitly labeled. Windows and Darwin rows can be established by source plus focused platform-configured spawner/helper tests; Linux execution must never be labeled a Windows/macOS runtime pass. Capture before/after screenshots later for the visible local Device status, paired with environment/exec logs. Screenshots alone cannot prove H/R precedence.

## Scope / acceptance

Affected surface is POSIX desktop startup and its locally spawned backend/provider/device processes. Web-only CLI startup, mobile-client startup, remote device-host environment provisioning, Windows/WSL hydration, contracts and RPC behavior are not modified. Web/mobile clients connected to the affected desktop-hosted server share its resulting local device availability; they do not need independent environment hydration implementations. No provider adapter changes or new entry points are introduced.

Matrix covers profile-only pair and singletons, inherited pair and both singletons including cross-key ROOT-vs-HOME, unset, empty and whitespace, path spaces/noise, Windows no-change, and Linux/Darwin selection. It distinguishes changed behavior from unchanged controls and defines actual app drive plus observation that cannot be repaired by terminal profile loading.

Friction: evidence | terminal startup may source the profile and falsely pass before-fix hydration | inspect the directly exec'd backend's filtered environment and pair with real local Device state.
Friction: fixture | SDK default-location/PATH fallback or a remote host can mask missing hydration | use nondefault distinct roots, exclude SDK directories from PATH, and inspect only the local host.
Friction: shell selection | env -i does not erase account-shell selection and system profiles can change HOME | record actual -ilc probe and verify throwaway profile capture before interpreting a row.

VERDICT: SUFFICIENT
