Skip to content

v1.57.7.0 fix(ios-qa): sliding tunnel cache so device sessions survive past 30s#1919

Closed
sternryan wants to merge 1 commit into
garrytan:mainfrom
sternryan:fix/ios-qa-sliding-tunnel-cache
Closed

v1.57.7.0 fix(ios-qa): sliding tunnel cache so device sessions survive past 30s#1919
sternryan wants to merge 1 commit into
garrytan:mainfrom
sternryan:fix/ios-qa-sliding-tunnel-cache

Conversation

@sternryan

Copy link
Copy Markdown
Contributor

What

Make the iOS QA daemon's device-tunnel cache a sliding idle-timeout instead of a flat 30s window, with a configurable default.

Why

getTunnel() in ios-qa/daemon/src/index.ts cached the CoreDevice tunnel for a flat 30s measured from bootstrap, and never refreshed the timer on use. A vision-driven agent loop (/ios-qa) reads a screenshot, reasons, then taps — that round trip routinely exceeds 30s. When the cache expires the daemon re-bootstraps, but re-bootstrap can't succeed against a still-running app: StateServer deletes its on-disk boot token at /auth/rotate (templates/StateServer.swift.templateremoveItem(atPath: bootTokenPath)), so bootstrapTunnel fails with boot_token_unavailable until the app is cold-relaunched.

Observed live: the bridge died roughly every 30 seconds mid-session and could only recover by killing + relaunching the app on the device.

Fix

Refresh cachedTunnelAt on every cache hit (sliding idle-timeout), so an active session holds the same live tunnel and rotated bearer for as long as it keeps working. Default idle window raised to 5 min (covers reason-then-act gaps) and made configurable via GSTACK_IOS_TUNNEL_CACHE_MS. No security change — the boot token still dies at rotate; this only avoids needless re-bootstrap of a healthy tunnel.

The default bump (30s → 5min) is the debatable knob; with the sliding behavior even a short default survives active use, but the reason-then-act gap between requests is what needs the longer window. Easy to dial back if you'd prefer.

Test

Adds a regression test in daemon-integration.test.ts asserting a burst of requests bootstraps the tunnel exactly once. bun test test/daemon-integration.test.ts → 5 pass, 0 fail.

Notes

  • Heads-up (pre-existing, not touched here): daemon-integration.test.ts imports afterEach at the bottom of the file, and the tailnet listener describe block errors with afterEach is not a function under bun — reproduces on main verbatim, so that block's tests don't run. Worth a separate fix (move the import to the top).
  • Fork PR: eval/E2E CI jobs won't get base-repo secrets, so they'll fail on empty-env auth. The daemon test + lint don't need secrets. Move base-side if you want full CI.

@trunk-io

trunk-io Bot commented Jun 8, 2026

Copy link
Copy Markdown

Merging to main in this repository is managed by Trunk.

  • To merge this pull request, check the box to the left or comment /trunk merge below.

After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here

@github-actions github-actions Bot changed the title fix(ios-qa): sliding tunnel cache so device sessions survive past 30s v1.57.7.0 fix(ios-qa): sliding tunnel cache so device sessions survive past 30s Jun 8, 2026
The daemon cached the CoreDevice tunnel for a flat 30s from bootstrap and
never refreshed the timer on use. A vision-driven agent loop that pauses to
read a screenshot and decide the next action exceeds 30s, the cache expires,
and getTunnel re-bootstraps. Re-bootstrap then fails permanently with
boot_token_unavailable because StateServer deletes the on-disk boot token at
/auth/rotate (StateServer.swift) — recovery requires a cold app relaunch.

Make the cache a sliding idle-timeout: refresh cachedTunnelAt on every reuse
so an active session keeps its live tunnel + rotated bearer. Default idle
window 5 min (was 30s), overridable via GSTACK_IOS_TUNNEL_CACHE_MS. No
security change: the boot token still dies at rotate.

Adds a regression test asserting a burst of requests bootstraps exactly once.
@sternryan
sternryan force-pushed the fix/ios-qa-sliding-tunnel-cache branch from d1cca02 to 71e044a Compare July 1, 2026 20:50
@sternryan

Copy link
Copy Markdown
Contributor Author

Rebased onto current main to clear the conflict — the PR is now mergeable. The only conflict was a stale chore(release): v1.57.7.0 commit (main is already at 1.58.5.0), which I dropped since versioning is the maintainer's call at merge time; the actual code fix applied with zero conflicts. Branch is now a single commit touching only ios-qa/daemon/src/index.ts + its integration test. Ran the affected test locally: 5 pass / 0 fail.

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