Skip to content

refactor(platforms): read host settings from HostEnvironment in platform and capture packages - #3777

Open
thymikee wants to merge 25 commits into
refactor/host-environmentfrom
refactor/host-environment-platforms
Open

thymikee wants to merge 25 commits into
refactor/host-environmentfrom
refactor/host-environment-platforms

Conversation

@thymikee

@thymikee thymikee commented Oct 17, 2026 •

Copy link
Copy Markdown
Member

Summary

Platform and capture packages read the host's environment and working directory from a HostEnvironment instead of process. No behavior change. Closes #3750 (part of #3747; stacked on #3763).

  • react-native, provision-kit, maestro and platform-android take the host or environment explicitly (Android reads its adb host port). Released agent-device/metro binds the process host at its boundary; the client passes its own.
  • platform-apple runner reads through its host port (hostEnvironment, R77 intact). The import-time container bundle ids are now resolveRunnerContainerBundleIds(env). Detached runner and bridge children get an explicit env and cwd.
  • capture-kit: the PNG worker starts with an empty env and reports unavailability through diagnostics only.
  • The app-log marker takes the request host's env from the daemon.
  • 80+ files, mostly tests binding a host.

Validation

pnpm check:affected --run --head f547d8895e: passed, exactCommit: true.

Device (earlier head): Android emulator and iOS 27.0 simulator passed runner snapshot, screenshot, PNG-worker diff, recording overlay, and app log. Not rerun after the explicit-host conversion; not run: runner detach/adopt across a daemon restart.

Remaining ambient commandHostEnvironment() reads are listed in the comment below.

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Size Report

Metric Base Current Diff
Installed (including dependencies) 5.38 MB 5.39 MB +1.7 kB
Package (unpacked) 5.38 MB 5.38 MB +1.7 kB
Package (download) 1.62 MB 1.62 MB +667 B

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 40.2 ms 40.0 ms -0.2 ms
CLI --help 75.4 ms 74.3 ms -1.2 ms

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 70 files

You’re at about 96% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread packages/capture-kit/src/png-worker-client.ts
Comment thread packages/maestro/src/daemon-port/run-script-http-child.ts Outdated
Comment thread src/platform-runtime-app-log-output.ts
Comment thread packages/provision-kit/src/install-source.ts
Comment thread packages/capture-kit/src/recording/swift-cache.ts Outdated
Comment thread packages/daemon/src/server/http/http-server-proxy.test.ts
Comment thread packages/platform-apple/src/runner/runner-process-launch.ts Outdated
@thymikee
thymikee force-pushed the refactor/host-environment-platforms branch from 457bd1b to bc06e18 Compare October 17, 2026 08:21
@thymikee

Copy link
Copy Markdown
Member Author

Thanks for the PR. At bc06e18, one change is needed before merge. The checks show 12 passing and none failing.

bindTestCommandHost in capture-kit is a verbatim copy of the one in packages/test-kit/src/host-environment.ts. That leaves two copies of the test-host binding policy. If one changes, for example to snapshot env through createTestHostEnvironment, the capture-kit suites would silently run with a different host shape than every other suite. Please move it to the package that owns withCommandHost. host-kit already ships fixture subpaths (./archive-fixtures, ./audio-probe-fixtures), so add a ./command-host-fixtures export there, import it from both capture-kit and test-kit, and delete the copy.

Not blocking, and you can take or leave these: prewarmPngWorker() runs at daemon startup outside a diagnostics scope, so a failed spawn latches warnedWorkerUnavailable and later scoped requests never report png_worker_unavailable (an unavailable worker should be reported in the first diagnostics scope that sees it, and wrapping the prewarm in a scope, as you deferred to #3752, would also work), and the two runCmdBackground env assertions in runner-process-launch.test.ts and snapshot-source/host.test.ts check call shape, because resolveSpawnEnv already gives an un-env'd spawn the same host.env.

On the open threads, this one on the PNG worker diagnostics is marked resolved but still applies. These threads are fixed at this head, so please resolve them: run-script-http-child.ts:9 passes process.env with no cwd lookup, platform-runtime-app-log-output.ts:26 uses the command host's redaction patterns, install-source.ts:87 passes { env }, swift-cache.ts:45-46 has the updated comment, http-server-proxy.test.ts:193 passes env, and runner-process-launch.ts:63-64 has the lookup inside the try.

The PR has merge conflicts with its base refactor/host-environment. I reviewed the patch against the merge-base and did not examine the conflicts. I read the daemon shutdown wrapper but did not run runner detach/adopt across a daemon restart, and I did not audit every async continuation for AsyncLocalStorage propagation. The live device evidence is your report at 457bd1b, which I did not reproduce.

Before merge, replace the copied helper with the shared host-kit export, then rebase onto the updated refactor/host-environment.

@thymikee

Copy link
Copy Markdown
Member Author

Rebased onto the narrowed base and converted configuration reads to explicit passing (f547d8895e, gate passed at that commit).

Ambient commandHostEnvironment() sites added by this PR: 35 in 22 files before, 10 in 6 files now.

Converted: react-native (host is a parameter; the client and agent-device/metro supply it), platform-android install artifacts (Android adb host port), provision-kit/maestro/capture-kit app-log (environment parameter), Apple install artifacts (option from the root executor), the daemon app-log marker (request host), and the Apple runner (13 files read through its host port as hostEnvironment, adapter bound once in core/runner-host.ts).

Still ambient, and why:

Both gaps close with one change: a HostEnvironment on createPlatformRuntimeGateway/PlatformRuntimeHost. I can do it as a follow-up on this branch if wanted.

@thymikee

Copy link
Copy Markdown
Member Author

Answering the 09:43Z review (bc06e18) at 70d321146d (gate passed at that commit).

Duplicate bindTestCommandHost (blocking): not changed yet, because the suggested homes both break a rule.

  • A host-kit ./command-host-fixtures export is a test export from a production package, which R83 ratchets down.
  • Importing test-kit from capture-kit's tests makes a cycle: test-kit depends on capture-kit (packages/test-kit/package.json; src/keyboard-occlusion.fixtures.ts and src/app-log-live-handle.ts import it), and docs/agents/test-authoring.md says packages test-kit depends on keep helpers local.

Waiting on the coordinator to choose: keep the local copy, or move those two test-kit fixtures out so capture-kit can depend on test-kit.

prewarmPngWorker latch (taken): the unavailable report no longer latches outside a diagnostics scope, so the first request scope that reaches the fallback reports png_worker_unavailable; a test covers it and fails against the old latch.

Spawn env assertions (left): they pin the explicit env/cwd that #3750 asks detached children to carry; the call shape is the contract there.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 2 files (changes from recent commits).

You’re at about 97% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread packages/capture-kit/src/png-worker-client.ts
@thymikee

Copy link
Copy Markdown
Member Author

At 70d3211 I found no blocking defects. Not blocking: prepareAndroidInstallArtifact in https://github.com/callstack/agent-device/blob/70d3211/packages/platform-android/src/install-artifact.ts#L24 now reads requireAndroidAdbHost().environment, which is bound to the live process env when the module loads, so a host whose env differs from process.env (for example the provider-scenario harness) would get process proxy settings on an Android network install. The rule is that every platform or capture read of host env or cwd comes from the injected HostEnvironment. You could pass environment in from the caller, as the Apple deployment executor does, or make the adb port's environment a getter over commandHostEnvironment().env. Take it or leave it.

The earlier findings from the bc06e18 review (#3777 (comment)) are fixed. I withdraw the duplicate bindTestCommandHost finding, because the local copy in capture-kit is the sanctioned one and a host-kit export would break the layering rule.

I read the code and did not run it on a device. The live device evidence is still your earlier report, and the runner change in this delta is a rename. I also did not trace that the production daemon's request host is the process host for every route, so "no CLI-visible change today" is inferred.

Smoke Tests was still running when I checked, so I cannot attribute CI yet. There are no conflicts. Before merge, wait for Smoke Tests to finish and reply to the open P3 thread on mid-request worker failure (#3777 (comment)): reportWorkerUnavailable() is not called in the PngWorkerUnavailableError branch before runSync, so the request that hit the failure reports nothing.

The other review threads are fixed at this head and you can resolve them: #3777 (comment) (prewarm no longer latches the report, and a test covers it), #3777 (comment) (run-script-http-child.ts passes process.env with no cwd lookup), #3777 (comment) (the delta does not touch the app-log output sink), #3777 (comment) (install-source.ts is not in the delta), #3777 (comment) (the swift-cache.ts doc comment was updated), #3777 (comment) (http-server-proxy.test.ts passes env), #3777 (comment) (runner-process-launch.ts reads the host inside the try).

@thymikee

Copy link
Copy Markdown
Member Author

Applied the review follow-ups at 320af90109 (gate passed at that commit).

  • Duplicate bindTestCommandHost: deleted the capture-kit copy; its tests import the helper from @agent-device/test-kit (new devDependency). To avoid the cycle, test-kit no longer depends on capture-kit: its keyboard fixture builds its own snapshot state, and createTestAppLogLiveHandle now implements the handle contract itself instead of calling capture-kit. No host-kit export was added.
  • PNG worker diagnostics: an unavailable worker is reported by the first diagnostics scope that reaches the fallback; a startup pre-warm outside a scope no longer latches the report. png-worker-client-unavailable.test.ts covers it and fails against the old latch.
  • Spawn env assertions: replaced the runner one with a behavior test: a fake xcodebuild on the bound host's PATH records the host's env and directory, which the old code (no explicit env/cwd) fails because the child would inherit the ambient scope. Deleted the snapshot-bridge one: inside a command host an explicit and an implicit env are identical, so no test can tell them apart there.
  • Threads: all seven review threads are resolved at this head, including the PNG one.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 15 files (changes from recent commits).

You’re at about 97% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread packages/test-kit/src/app-log-live-handle.ts Outdated
Comment thread packages/platform-apple/src/snapshot-source/host.test.ts
@github-actions

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://callstack.github.io/agent-device/pr-preview/pr-3777/

Built to branch gh-pages at 2026-10-17 12:00 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

@thymikee

Copy link
Copy Markdown
Member Author

Coordinator review at 320af90: please revert the test-kit cycle-break and keep the capture-kit-local bindTestCommandHost.

To drop test-kit's dependency on capture-kit, this head re-implements production behaviour in test-kit:

  • createTestAppLogLiveHandle (packages/test-kit/src/app-log-live-handle.ts) now reproduces capture-kit's createAppLogLiveHandle contract: idempotent finish and cleanup, disposal that confirms cleanup, and the AppError it throws. Suites that use it no longer exercise the real handle, and the two will drift.
  • keyboard-occlusion.fixtures.ts replaces makeSnapshotState with its own state builder.

Both trade one three-line duplicated test helper for duplicated production semantics. docs/agents/test-authoring.md already sanctions a local helper in a package that test-kit depends on, and the 11:38Z review withdrew the duplicate-helper finding on that basis.

Requested shape:

  • Restore test-kit's capture-kit dependency, along with its createAppLogLiveHandle and makeSnapshotState uses.
  • Keep the local bindTestCommandHost in capture-kit.
  • Make both copies build the host with the production captureHostEnvironment(process), so the host shape has one owner.

The PNG-worker and spawn-env changes in this push are good; keep them.

@thymikee
thymikee force-pushed the refactor/host-environment-platforms branch from 320af90 to 3fbabc3 Compare October 17, 2026 12:29
@thymikee

Copy link
Copy Markdown
Member Author

Applied the coordinator's ruling at 3fbabc3e8e (rebased onto the base after #3763's last merge of main; gate passed at that commit).

  • Duplicate bindTestCommandHost: no copy is left. Per docs/agents/test-authoring.md ("packages that test-kit depends on keep helpers local"), capture-kit does not import test-kit, and test-kit depends on capture-kit again. Instead capture-kit now takes the host explicitly (Swift cache root, tool env, script search directory, and the recording, overlay, contact-sheet and playable-video paths), so its suites need no bound command host and the local helper is deleted. The tests' hosts come from captureHostEnvironment(process) (createTestHost), the one owner of the host shape. The root recording seams (finalizer, Android recording, contact-sheet command) supply commandHostEnvironment().
  • Single-owner capture in test-kit: I changed bindTestCommandHost to captureHostEnvironment(process) and reverted it. captureHostEnvironment copies the environment when the test starts, so every suite that calls vi.stubEnv afterwards stopped seeing it: 82 tests in 32 files failed (platform-apple, linux, harmonyos, web, cloud-connect and others). The live-environment binding stays until those suites pass a host explicitly.
  • Spawn-env assertions: the runner one is now a behavior test (a fake xcodebuild on the bound host's PATH records its env and directory; the old code fails it). The snapshot-bridge call-shape assertion is deleted. The explicit env/cwd on detached children is the refactor(platforms): read host settings from HostEnvironment in platform and capture packages #3750 contract: the child can't receive a live host object.

@thymikee
thymikee force-pushed the refactor/host-environment-platforms branch from 3fbabc3 to 88a82c3 Compare October 17, 2026 12:41

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 2 files (changes from recent commits).

You’re at about 97% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread packages/capture-kit/src/png-worker-client-unavailable.test.ts Outdated
@thymikee

Copy link
Copy Markdown
Member Author

Applied the 11:38Z recommendation at f3ddfe3add (on the current base; gate passed at that commit).

  • Android install environment: prepareAndroidInstallArtifact no longer reads requireAndroidAdbHost().environment (bound to the process env at module load). It takes environment from its caller, as the Apple executor does; the root Android deployment executor passes the command host's env. The cancellation test now passes an env with a proxy the adb host lacks and asserts the download request carries it.
  • Single-owner host capture: see my earlier comment: capture-kit builds its test hosts with captureHostEnvironment(process) and needs no bound host; test-kit's bindTestCommandHost stays live because snapshotting breaks 82 tests in 32 suites that use vi.stubEnv.

@thymikee

Copy link
Copy Markdown
Member Author

Reply to the review at 320af90109: that head is old. At 1efe26fec7 (gate passed, base unchanged) the state is:

  • test-kit cycle-break reverted: test-kit's tree is identical to the one before the cycle-break (git diff 70d321146d -- packages/test-kit is empty). It depends on capture-kit again, createTestAppLogLiveHandle wraps capture-kit's constructor, and the keyboard fixture uses makeSnapshotState. capture-kit has no test-kit dependency.
  • Local bindTestCommandHost in capture-kit: not restored, because nothing would call it. capture-kit no longer reads the command host: its Swift cache and recording code take the host as a parameter, and its tests build one with captureHostEnvironment(process) (createTestHost). A bound host there would be dead code.
  • Host capture in both copies: capture-kit's does. test-kit's bindTestCommandHost still uses the live process env: switching it to captureHostEnvironment(process) fails 82 tests in 32 suites that call vi.stubEnv after the test starts. That needs those suites to pass a host explicitly first.
  • Android install environment: applied in f3ddfe3add (environment comes from the caller).
  • Kept: the PNG-worker and spawn-env changes.

Say if you still want the unused local helper restored.

@thymikee

Copy link
Copy Markdown
Member Author

Rebased onto refactor/host-environment (d8a9bd4) at 867301dfed; gate passed at that commit. One conflict, in the provider scenario harness: the base now has its own scenario env override and iOS recording fix, so I took its version. Android and macOS recording scenarios set the Swift cache on the process before the harness starts, so the harness host still carries AGENT_DEVICE_SWIFT_CACHE_DIR from the process; without it the full provider-integration project failed 5 tests.

@thymikee
thymikee force-pushed the refactor/host-environment-platforms branch from 2c23022 to 867301d Compare October 17, 2026 14:27
thymikee and others added 6 commits October 17, 2026 16:27
…ngs from the command host

The PNG worker starts with an empty environment and reports unavailability
through diagnostics instead of a process warning.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The detached companion process starts with an explicit env and cwd, and its
process entry captures its host from its own spawn environment.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…'s host

The helpers moved from process reads to the command host, so the released
agent-device/metro functions and the client bind one at their boundary.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… explicit env and cwd

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The runner reaches the host through its port. The container bundle ids were
fixed at import time from the process environment; they are now resolved from
the host a caller passes in.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
thymikee and others added 19 commits October 17, 2026 16:27
…its own host

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ing the command host

The eager-closure budgets forbid the host-kit command import in the install
source download, the app-log file policy and the Maestro HTTP helper, so their
callers pass the environment they already hold. Client tests that change PATH
give the client a host instead.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Redaction patterns come from the command host, the maestro child reads its
own environment without a working-directory lookup, local ~ install sources
expand against the host environment, and the runner launch resolves its host
inside the try that closes its log descriptor.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…he command host

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…e reads configuration

Android install artifacts read the environment from the Android adb host port,
Apple install artifacts take it as an option from the root deployment executor,
and the Apple runner names its host port member hostEnvironment.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… directory

The daemon's host snapshots the environment when it starts, so a scenario that
isolates the Swift cache or its PATH sets them before the harness is created.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ostics scope that sees it

A startup pre-warm runs outside any scope, where the event is dropped; it no
longer latches the one-time report.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…test the runner's bound host behaviorally

test-kit no longer depends on capture-kit: its app-log live-handle alias is
gone (callers use the capture-kit constructor) and its keyboard fixture builds
its own snapshot state. capture-kit's tests take the helper from test-kit.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… its own entry

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…apture-kit

Replaces the capture-kit subpath entry, which grew the declared export surface.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ecording tooling

capture-kit no longer reads the command host, so its suites need no bound host
and its local test-host copy is gone; the root recording seams supply the host.
test-kit keeps depending on capture-kit again, and capture-kit does not depend on
test-kit.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…equest

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The Android adb host port's environment is bound at module load, so a host
whose environment differs from the process's got the process's proxy settings
on a network install.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ectory

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ng test

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…or scenarios that set it on the process

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@thymikee
thymikee force-pushed the refactor/host-environment-platforms branch from 867301d to 8b5e97f Compare October 17, 2026 14:37

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant