Skip to content

fix(browserstack): carry the full provider-allocation config over the lease_allocate envelope (#2494) - #2495

Merged
thymikee merged 5 commits into
mainfrom
fix/browserstack-provider-session-metadata-2494
Sep 11, 2026
Merged

thymikee merged 5 commits into
mainfrom
fix/browserstack-provider-session-metadata-2494

Conversation

@thymikee

@thymikee thymikee commented Sep 11, 2026 •

Copy link
Copy Markdown
Member

Summary

--provider-project, --provider-build, and --provider-session-name were captured in the connection profile and reach the daemon on the line transport (which forwards the whole request), but the compact JSON-RPC lease_allocate envelope forwarded only providerApp. Over an HTTP/remote daemon the daemon's lease-lifecycle provider received no session metadata, so BrowserStack sessions landed in "Untitled Project"/"Untitled Build" with an empty name. Project and build names are immutable after creation, so there is no post-hoc workaround.

The envelope is a hand-written projection, so it also dropped the fields prepareSession needs to create the session at all — device selection (platform, device), providerOsVersion, and the configured device-feature/AWS knobs — meaning a fresh remote allocation still failed before the names could take effect. Fix at the construction seam: one shared projection, readLeaseAllocateProviderFlags, now carries the full provider-allocation set that both the client's producer and the daemon's consumer read, so the two transports agree and a sibling cannot be dropped again (the drift #2110 left when it forwarded providerApp alone). The key list is pinned exhaustive against CloudProviderProfileFields. Additive on the wire: each field rides only when supplied; a request carrying none is byte-identical.

connect browserstack --provider-app bs://… --provider-project MyProject \
  --provider-build Build-2026-09-11 --provider-session-name "smoke — iOS"

Closes #2494

Validation

Tested at commit d99226b. pnpm check:affected --run green (unit-core 3172 tests, provider scenarios, check:daemon-wire-compat: 2 changed, protocol unchanged — acked additive in ledger.json).

Regression coverage at each seam: a contract unit test for the shared projection, a producer test that the flags ride the lease_allocate payload, an HTTP reconstruct check (daemon-http-lease-allocate.test.ts), and cloud-webdriver-lease-http.test.ts, which drives the exact client envelope through a real daemon HTTP server into the real BrowserStack prepareSession and asserts the POST /wd/hub/session capabilities (deviceName/os_version/app + bstack:options.{projectName,buildName,sessionName,…}). Narrowing the projection back to the label trio makes that end-to-end test fail, so it is a real guard. tsc (contracts + root), oxlint, oxfmt --check, check:layering, and check:production-exports pass. Not device-verified against live BrowserStack.

…nvelope

`--provider-project`, `--provider-build`, and `--provider-session-name` are
stored in the connection profile and reach the daemon on the line transport
(which forwards the whole request), but the compact JSON-RPC lease envelope
carried only `providerApp`. Over HTTP/remote daemons the daemon's lease-lifecycle
provider therefore saw no session-naming metadata and created BrowserStack
sessions as "Untitled Project" / "Untitled Build" with an empty name.

Read the four provider session-metadata flags through one shared projection
(`readLeaseAllocateProviderMetadata`) used by both the client's
`buildHttpRpcPayload` and the daemon's `toLeaseDaemonRequest`, so the transports
agree and the producer and consumer cannot drop a sibling again.

Closes #2494
@github-actions

github-actions Bot commented Sep 11, 2026 •

Copy link
Copy Markdown
Contributor

Size Report

Metric Base Current Diff
Installed (including dependencies) 4.48 MB 4.48 MB -1.1 kB
Package (unpacked) 4.48 MB 4.48 MB -1.1 kB
Package (download) 1.32 MB 1.32 MB -310 B

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 26.9 ms 26.7 ms -0.2 ms
CLI --help 75.1 ms 78.2 ms +3.2 ms

…n file

The provider-scenario daemon-http-server test is over the 1000-line tripwire
and may not grow; the lease_allocate metadata assertion now lives in
daemon-http-lease-allocate.test.ts, mirroring the lease projection in
http-server.ts.
@thymikee

thymikee commented Sep 11, 2026 •

Copy link
Copy Markdown
Member Author

The shared naming-field projection is correct, but the claimed fresh HTTP BrowserStack path is still incomplete at 7e36094. readLeaseAllocateProviderMetadata drops platform, device and providerOsVersion; toLeaseDaemonRequest reconstructs no other flags, and leaseLifecycleContext passes that bag directly to the provider. BrowserStack prepareSession requires those fields before creating the session, so a fresh remote allocation still fails before the names can take effect. Configured BrowserStack device-feature flags are also dropped by this projection. This omission predates the PR, but it is at the same allocation boundary and blocks the advertised fix. Carry the required provider configuration through the shared contract and extend the HTTP regression through the real lease/provider preparation path to assert the outgoing session capabilities. The current test substitutes handleRequest and only proves the intermediate flags bag. The iOS smoke failure is RUNNER_BUSY at the WebView wait and appears unrelated.

@thymikee

Copy link
Copy Markdown
Member Author

Addressed in 176e8eb.

The shared projection is now readLeaseAllocateProviderFlags and carries the full set prepareSession reads, not just the labels: device selection (platform, device), providerOsVersion, the app, the session-naming fields, the eight BrowserStack device-feature knobs, and the AWS knobs. Producer and consumer already shared the one reader, so both transports move together. The key list is pinned exhaustive against CloudProviderProfileFields, so a new provider field that skips the projection fails to compile instead of silently breaking a fresh remote allocation.

The reviewer's "carries the required provider configuration" and "assert the outgoing session capabilities" points are both in. New cloud-webdriver-lease-http.test.ts drives the exact client envelope (buildHttpRpcPayload) over a real node:http request into a real createDaemonHttpServer + createRequestHandler, through the real BrowserStack prepareSession, and asserts the POST /wd/hub/session body — deviceName/os_version/app and bstack:options.{projectName,buildName,sessionName,deviceOrientation}. The provider fake hijacks globalThis.fetch, so the daemon RPC is spoken over node:http to avoid the interception.

Proof it is a real guard, not a green-by-construction test: narrowing the projection back to the label trio makes that end-to-end test fail (the daemon returns not-ok because prepareSession rejects the missing --device/--platform). The daemon-http-lease-allocate.test.ts reconstruct check is broadened in step, now that a boolean (providerNoResignApp) and device-feature/AWS keys ride too. Per the tripwire note, daemon-http-server.test.ts was left at your version and not grown.

Wire digests re-pinned; ack rationale updated. check:affected --run green (3172 unit tests, daemon-wire-compat "2 changed, protocol unchanged"). The iOS RUNNER_BUSY at the WebView wait looks unrelated, as you said.

@thymikee thymikee changed the title fix(browserstack): forward provider session metadata over the lease_allocate envelope (#2494) fix(browserstack): carry the full provider-allocation config over the lease_allocate envelope (#2494) Sep 11, 2026
… lease envelope

The lease envelope named only the session-label fields, so a fresh remote
allocation still failed in prepareSession before the names could take effect:
device selection (platform, device), providerOsVersion, and the configured
device-feature/AWS knobs were dropped on the HTTP transport, while the line
transport forwards the whole request for free.

readLeaseAllocateProviderMetadata becomes readLeaseAllocateProviderFlags and
projects the full set the lease-lifecycle provider reads, pinned exhaustive
against CloudProviderProfileFields so a new field cannot silently miss it. The
producer and consumer already share this reader, so both transports agree.

Covered end to end in cloud-webdriver-lease-http.test.ts: the exact client
envelope is driven through a real daemon HTTP server into the real BrowserStack
prepareSession, asserting the capabilities that reach the hub.
@thymikee
thymikee force-pushed the fix/browserstack-provider-session-metadata-2494 branch from 176e8eb to d99226b Compare September 11, 2026 15:28
@thymikee

Copy link
Copy Markdown
Member Author

No remaining findings at d99226b. The shared projection carries the full allocation configuration, and the HTTP regression now reaches the real BrowserStack preparation path and checks the outgoing capabilities. Ready for human review; live BrowserStack acceptance was not tested, and the iOS WebView RUNNER_BUSY failure appears unrelated to this transport change. Revalidate iOS smoke before merge.

@thymikee thymikee added the ready-for-human Valid work that needs human implementation, judgment, or maintainer merge label Sep 11, 2026
@thymikee
thymikee merged commit d80fb35 into main Sep 11, 2026
17 of 18 checks passed
@thymikee
thymikee deleted the fix/browserstack-provider-session-metadata-2494 branch September 11, 2026 16:21
@github-actions

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-09-11 16:21 UTC

thymikee pushed a commit that referenced this pull request Sep 14, 2026
…s are honoured (#2608)

BrowserStack's hub classifies a create-session request as legacy JSON Wire as soon as
any legacy top-level key (`device`, `os_version`, `app`) is present. In that mode it
reads project/build/name from the legacy top-level keys, which agent-device never
sets, and ignores `bstack:options` entirely — so every session still landed in
"Untitled Project" / "Untitled Build" with an empty name after #2495 carried the
`--provider-project` / `--provider-build` / `--provider-session-name` flags through to
the provider (#2494).

`buildBrowserStackCapabilities` now emits `appium:deviceName`, `appium:platformVersion`
and `appium:app` instead, leaving `bstack:options` as the only vendor block. Verified
against live App Automate sessions: the same `bstack:options` payload is ignored with
the legacy keys present and honoured without them, independent of Appium version.

Integration assertions updated to the W3C shape and extended to reject any legacy key
at the top level.

Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-for-human Valid work that needs human implementation, judgment, or maintainer merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BrowserStack: --provider-project, --provider-build, --provider-session-name not forwarded to daemon in lease_allocate RPC

1 participant