Repository navigation
Conversation
This was referenced Nov 4, 2026
muvaf
force-pushed
the
codex/limrun-sync-install
branch
from
November 4, 2026 20:32
092c6fa to
a95049b
Compare
muvaf
force-pushed
the
codex/limrun-native-runner
branch
from
November 4, 2026 20:32
c3c281f to
0ae3dc2
Compare
…allstack#4223) * fix(daemon): aim find focus at the parent-owned touch point The find focus seam dispatches its own point instead of re-entering the interaction leaf, so it skipped the touch-point decision the acting paths run. A match fully tiled by interactive children now refuses before dispatch, and a child owning the center moves the aim to free parent space. Missing or zero-area rects get the typed invalid-bounds refusal. Move resolveNodeTouchPoint to @agent-device/selectors/node-touch-point so the daemon and command-runtime share one owner, and close the parentOwnedTouchPoint cell of the find-focus-dispatch guarantee row. Refs callstack#3734 * chore(gates): declare the selectors node-touch-point subpath The package-boundaries enumeration lists every selectors subpath, so the new export has to be declared for the R11 layering rule to accept it. * fix(contracts): describe the find focus touch point in its waivers The offscreen and nonHittable waiver reasons still said the seam dispatches a rect center. * fix(daemon): aim find focus and type at a nested text field The touch-point rule treats a nested text field as a competitor to avoid, so a match wrapping a field reported focus success beside the field and the typed text then went wherever the focus already was. A sole enabled, visible field now becomes the target, with the same touch-point, covered and keyboard checks. Several fields refuse with AMBIGUOUS_MATCH and the field candidates. Refs callstack#3734 * style(test): format the find focus contract scenarios * fix(daemon): keep find focus on the field the locator named A disabled field promotes to its parent on iOS, and the parent's other field then counted as the sole one, so find type wrote into a field the locator never named. The located field now decides first: an enabled one stays the target and a disabled one refuses with target_disabled, as does a match holding only disabled fields. Count only outermost on-screen typed fields, so a hosted text field, a scrolled-away field and an untyped child no longer change the target. The rule moves to the find description so MCP clients see it, and it names web text boxes as unrecognized. Refs callstack#3734 * fix(daemon): refuse a disabled field reached through promotion The disabled check only read the located node, so a same-rect wrapper over a disabled field was still focused, and a label inside one was promoted past it to an enabled sibling. find type then wrote into another field and reported success. The field decision now walks from the located node to the promoted one, and the nearest field on that path is the named field. The find help, selector docs and nonHittable waiver now state that a named text field stays the target. Refs callstack#3734 * fix(selectors): count only measurable fields and read password facts A field with no usable bounds counted as on-screen, so it could take the focus or turn a single field into an ambiguity before the touch point could reject it. A password fact on an unrecognized type was ignored, so focus went to the container instead. Say in the help and docs that a named field wins over its neighbours and that target_disabled is a reason, and record why the nested-field refusal follows find's refusal contract for printed refs. Refs callstack#3734 * docs(selectors): split the find focus rule and name the covered refusal Refs callstack#3734 * docs(selectors): qualify which fields refuse find focus Refs callstack#3734 * docs(selectors): name the covered refusal's reason Refs callstack#3734
…tack#4211) * fix(ios): deliver simulator edge swipes as screen-edge touches XCTest synthesized touches carry no digitizer edge mask, so on an iOS simulator gesture swipe left-edge and right-edge never started a UIScreenEdgePanGestureRecognizer or the navigation edge pop gesture. Edge swipe presets now plan a screen-edge contact. On an iOS simulator the runner dispatches it as one HID touch with the edge mask, through the touchscreen service of the app's display. Other gestures and physical devices keep their current paths. Fixes callstack#4208 * fix(ios): report an edge touch whose HID event cannot be created A frame that failed to build was dropped while the gesture still reported success. Fail before touch-down, lift early after a failed move, and return the reason instead. * fix(ios): send simulator edge touches as the system's locked edge swipe The edge recognizer on a rotated panel, such as the iPhone Duo inner panel, never began: FromEdgeFlat/Tip touches carry no swipe direction, and a fast first move left the edge region in one frame. A hand swipe on the simulator carries SwipeLocked and the native swipe direction on every frame and starts slowly. Send the same mask, ease the contact in along the planned path, and use the display's oldest touchscreen service when a window adds another. * fix(ios): pick the touchscreen sender only from readable registry IDs A service whose registry ID could not be read still counted as a match, so a later valid service could leave the sender at 0. * fix(ios): end every simulator edge contact with a reserved terminal event A touch-up that could not be built after touch-down left UIKit with an active finger. The edge contact owner now builds a cancel event before touch-down and, after touch-down, sends the touch-up or that cancel on every exit. Unit tests fail each HID constructor in turn and check what reaches the event system. * fix(ios): report the edge swipe schedule the runner ran Edge flings ran an eased HID schedule but reported endpoint-hold. The runner execution policy now resolves the contact and its schedule together and returns the schedule as executionProfile. The daemon keeps a reported schedule and infers the planned profile only when a backend reports none. ADR 0013 records edge-ease-in and the reporting rule. * fix(ios): keep the gesture schedule the backend reports Address review: a backend-reported executionProfile is what ran, so the daemon keeps any reported string and reports the planned profile only when a backend reports none. Drop the TS schedule list that existed only to filter it. The cancel test also checks that touch-down and each move before the cancel touched. * refactor(ios): keep the edge contact owner private to its file Address review: the contact function and HID table were exported only so unit tests could inject faults. Make them static and compile the test facade in the same file under AGENT_DEVICE_RUNNER_UNIT_TESTS, so the packaged runner ships no fault-injection interface. * refactor(ios): keep the edge contact test facade out of the packaged runner Address review: the packager copies Objective-C sources verbatim, so the AGENT_DEVICE_RUNNER_UNIT_TESTS block in RunnerSimulatorEdgeTouch.m shipped and the package leak guard rejected it. Move the contact owner into a header whose declarations all have internal linkage, include it from the runner and from the test facade in UnitTests/, and keep the shipped source free of the unit-test macro. * fix(ios): select a simulator touchscreen that reports no Transport Reported on iOS 18.1 and 26.5 simulators: the display's touchscreen service passed the usage, display and registry checks but read no Transport, so edge swipes failed with "no simulator touchscreen HID service". The oldest CoreDevice touchscreen still wins. Without one, the display's only remaining candidate is used, and several are refused as ambiguous with how each Transport read (missing, other type, other value). The no-candidate error now names the stage that rejected the touchscreens. Unit tests cover the reported service, CoreDevice precedence, duplicates, ambiguity and the error. * fix(ios): use a touchscreen without Transport only as the display's sole one Address review: the fallback accepted a sole candidate whatever its Transport read. Only a missing Transport has been reported, so a touchscreen with a Transport of another value or type is no longer a candidate, and the display refuses to pick a sender while one is present without a CoreDevice touchscreen. The runner logs the selected service and its properties as AGENT_DEVICE_RUNNER_EDGE_TOUCHSCREEN so a host run shows which sender an edge touch used. * chore: retrigger CI
…callstack#4183) * feat(lease): name the iOS simulator mode in a proxy lease device spec A backend in front of the daemon (simlock) can lease an iOS simulator slim or full, but a proxy allocate could not ask for one: the device spec had no key for it, so every lease got the backend's default. Apple Pay and Wallet (PassKit) flows need a full simulator on an otherwise-slim farm. `deviceMode` joins osVersion, imageTag and physical as a device-spec key: the CLI's --device-mode, the SDK's leases.allocate and MCP send it on the allocate, and a daemon refuses it with device-spec-unsupported like the others. Co-Authored-By: Claude Opus 5.5 <[email protected]> * test(lease): cover the CLI and MCP half of the device-mode route The open --device-mode route had no test: deleting the spec key from the materialized flags, the non-by-spec allocate or the connect proxy profile left the suite green. Extend the open-by-spec, re-allocate, profile and MCP cases and iterate LEASE_DEVICE_SPEC_KEYS. The two hand-written key lists in the CLI become loops over the same constant, so the next key needs no edit there. lease.device does not report deviceMode; the docs now say so. Co-Authored-By: Claude Opus 5.5 <[email protected]> * fix(lease): refuse a grant that reports another device mode than requested lease.device did not report deviceMode, so a backend could lease a slim simulator for a full request with no error. The lease can now report the granted mode, and the CLI proxy connection releases a new lease that reports another mode than the request named, and fails with device-spec-unsupported. A lease that reports no mode is still accepted, because no simlock reply reports one yet and refusing it would break valid full leases. The docs state that the mode is then unconfirmed. Co-Authored-By: Claude Opus 5.5 <[email protected]> * docs(lease): say a refused grant that reports reuse is not released Co-Authored-By: Claude Opus 5.5 <[email protected]> * fix(lease): refuse only a slim grant for a full request Comparing the modes for equality would release a valid lease once a backend reports the mode: simlock's ADR 0007 makes full a guarantee and slim best effort, so a slim request can be granted a full simulator and that is never an error. Only slim granted for full breaks the request. Co-Authored-By: Claude Opus 5.5 <[email protected]> * docs(lease): qualify the release claim for a reused allocation A refused allocation that reports reuse is not tracked for release, so it has no release and no details.released. Co-Authored-By: Claude Opus 5.5 <[email protected]> * test(lease): give the device mode its own iOS allocation case deviceMode was sent on an Android allocation. The forwarding held, but the request was not a valid one, so the mode moves to an iOS fixture. Co-Authored-By: Claude Opus 5.5 <[email protected]> * docs(lease): exclude reused allocations from the help's release claim Co-Authored-By: Claude Opus 5.5 <[email protected]> --------- Co-authored-by: Claude Opus 5.5 <[email protected]>
…k#4220) * fix(cli): give cloud device leases a 10 minute TTL Limrun and Revyl leases fell to the daemon's 60 second default, so a session ended after one minute without a command. A new holdsCloudDevice capability selects the cloud device lease TTL, shared with cloud WebDriver leases. Co-Authored-By: Claude <[email protected]> * test(cli): cover the cloud device lease TTL in the policy Co-Authored-By: Claude <[email protected]> * test(cli): expect the cloud device capability in provider declarations Co-Authored-By: Claude <[email protected]> * fix(cli): one cloud lease TTL rule, validated capability, daemon ceiling clamp Co-Authored-By: Claude <[email protected]> * docs(limrun): state the observed instance inactivity window Co-Authored-By: Claude <[email protected]> * fix(cli): share the five-minute remote device lease TTL with cloud device leases Co-Authored-By: Claude <[email protected]> * refactor(cli): one five-minute idle TTL for every remote device lease Co-Authored-By: Claude <[email protected]> * docs(limrun): state the three minute instance inactivity timeout Co-Authored-By: Claude <[email protected]> * fix(limrun): create instances with the remote lease idle window as inactivity timeout Limrun's API default is 3 minutes, so a created instance ended before its lease. Co-Authored-By: Claude <[email protected]> * test(limrun): expect the inactivity timeout in the Android create input Co-Authored-By: Claude <[email protected]> --------- Co-authored-by: Claude <[email protected]>
muvaf
force-pushed
the
codex/limrun-sync-install
branch
from
November 5, 2026 10:35
a95049b to
a6a2a7c
Compare
muvaf
force-pushed
the
codex/limrun-native-runner
branch
from
November 5, 2026 10:35
0ae3dc2 to
442a6ce
Compare
…callstack#4232) Every snapshot round trip attached its own `bridgeProcess.wait.then(...)` to notice a bridge exit. The daemon keeps one bridge per Simulator and, while it keeps using it, that bridge does not exit, so `wait` stays pending for the bridge's whole life. A reaction on a pending promise cannot be removed, so each round trip's closure context stayed reachable until the bridge exited: the frame decoder with its buffered response chunks, the request frame, and the parsed envelope. Profiling a real daemon measured about 74 KB retained per snapshot, linear and unbounded. The bridge process now carries one exit reaction. A per-process registry in the transport holds the round trips that are waiting, a round trip joins it when it starts and leaves it in `finish`, and the one reaction tells whoever is in the set. An exit still reaches an in-flight round trip as the `process-crash` from `bridgeProcessExited`, and a failed status read as the error from `asSnapshotSourceError`. A settled round trip is no longer reachable from the process, so the `settled` guards in the two exit handlers are gone. The registry entry is dropped once the status settles. A round trip that starts after the exit registers on the settled promise, as before, and fails as a process crash on its next turn instead of waiting out its deadline. transport.test.ts drives `roundTripSnapshotBridge` against a fake process whose exit status counts every reaction registered on it. It pins one reaction across 21 round trips, the exit and the failed status read of an in-flight round trip, an exit before a round trip starts, and silence toward a round trip that already settled. Co-authored-by: Claude Sonnet 5.5 <[email protected]>
muvaf
force-pushed
the
codex/limrun-native-runner
branch
from
November 5, 2026 12:42
442a6ce to
7e18ad0
Compare
…callstack#4224) * refactor(apple): expose runner preparation and shared cancellation helpers * feat(limrun): replace iOS AX automation with the shared XCTest runner
…kend runner (callstack#4184) * feat(screenshot): select the XCTest runner capture with --capture-backend runner On iOS simulators with a Dynamic Island, simctl io screenshot draws the island into every capture (no --mask policy removes it), while XCUIScreen, which Maestro and other XCTest tools capture with, leaves it out. A visual baseline made by such a tool therefore cannot be matched by agent-device's default capture. The runner capture already existed for the Maestro port behind an internal flag. screenshot --capture-backend runner (SDK and MCP: captureBackend: 'runner') selects it on Apple targets. It is refused on other platforms and with --pixel-density, since the runner encodes the device density. The Maestro port now uses the public flag; flags.maestro.screenshotCaptureBackend is removed. Co-Authored-By: Claude Opus 5.5 <[email protected]> * fix(screenshot): refuse a runner capture on a surface the helper owns The screenshot admission accepted --capture-backend runner on every Apple surface, but runAppleScreenshot returns the macOS helper capture before it reaches the runner branch, so the request reported success with another backend. The Apple binding now declares runner support from the selected surface and app backend, and assertScreenshotCaptureSupport refuses it. --normalize-status-bar was dropped the same way, so it is refused with the runner like --pixel-density. The MCP input gains its schema description. Co-Authored-By: Claude Opus 5.5 <[email protected]> * fix(screenshot): make every capture owner declare its capture backends An owner that declared nothing accepted --capture-backend runner, so a provider iOS session such as Limrun reported success with a provider screenshot. Every screenshot binding now has to declare its backends and its own explanation, and the existing admission check refuses a backend the owner does not list. The daemon's Apple-only check moves into that one place. Maestro's comparison capture asked for the runner through the public flag, which would now fail on a provider. It goes back to its internal maestro.screenshotCaptureBackend, read as a preference: the runner where the owner declares it, otherwise the owner's default capture, as before. Co-Authored-By: Claude Opus 5.5 <[email protected]> * fix(screenshot): keep Maestro's runner preference on the daemon path The daemon passes an own captureBackend key, undefined for a Maestro request, and it overrode the preference in the binding, so a local iOS comparison capture fell back to simctl and drew the Dynamic Island back in. An explicit backend still wins. The preference moves off the shared ScreenshotOptions onto the binding input, since the binding strips it before the interactor. The daemon-level Android refusal case is back, now at the capture owner, and WebDriver's refusal has a case. Co-Authored-By: Claude Opus 5.5 <[email protected]> * test(screenshot): check the destination of a preferred-backend capture Co-Authored-By: Claude Opus 5.5 <[email protected]> * fix(screenshot): align explicit runner capture with the default under a runner-backed provider Under a tool provider whose simulator screenshots go through the runner, the default capture asked the runner for fullscreen, but an explicit --capture-backend runner did not. The default now lives in the runner capture, so both routes share it. An explicit fullscreen value still wins, and captures outside such a provider are unchanged. Co-Authored-By: Claude Opus 5.5 <[email protected]> * fix(screenshot): keep a macOS runner capture out of the provider fullscreen default The default checked only device.kind, so a macOS device shaped as a simulator got fullscreen under a runner-backed provider. It now uses the same predicate as the capture path. Co-Authored-By: Claude Opus 5.5 <[email protected]> * test(screenshot): name what the macOS fullscreen case checks Co-Authored-By: Claude Opus 5.5 <[email protected]> --------- Co-authored-by: Claude Opus 5.5 <[email protected]>
callstack#4218) * fix(daemon): settle orphaned recording records when a recording starts A device that restarts mid-recording can leave its screen recording record open with a pending cleanup when the daemon's own recovery ran while the device was still booting. Only daemon start and a device claim reconciliation retried it, and a recovered record stop cannot finish it without a playable artifact, so once the claim was gone every later record start on that device was refused with cleanup-unconfirmed. The start preflight now lists the records it refuses on, and record start runs the exact-owner recovery for the ones whose session the daemon no longer holds, so the settle set cannot drift from the refusal. A recovered stop and this settle share one recovery-control helper. A record whose cleanup cannot be confirmed, or whose session is still held, still refuses the start, and the refusal names that session. * fix(capture-kit): keep the start-time settle on the start device A start refuses on a non-completed record at its own session path even when that record names another device. The settle recovered that record through the start device, so a recording still running on the other device could have its only record completed from the wrong one. The settle now recovers only records on the start device. A record that another device left under the same session name still refuses, and the refusal names that device: stop it in that session, or start under another session name. * fix(capture-kit): finish an orphaned record before a start cleans it The start-time settle ran exact-owner recovery, which force-cleans any capture it can reattach. On an iOS simulator that discarded a video a record stop in the old session could still export, so a new start on the same simulator lost the earlier recording. The settle now does what the record's own stop would: it finishes the record, so the export lands at its output path and a later record stop in that session returns it. Only a record that cannot finish, such as an Android recording whose device restarted, falls back to exact-owner recovery. * docs(command-surface): say the start-time fallback discards a recording The record start help and the changeset described the fallback for a record that cannot finish only as the exact-owner recovery a daemon restart runs. Say that this recovery discards what the recording left, so a caller knows a start can delete an unfinished recording. * refactor(capture-kit): tag start conflicts with their device listDurableCaptureStartConflicts already decided whether each record is on the start device, then dropped that fact, so the start-time settle and the refusal wording each compared device identities again. Return onStartDevice with each record and read it in both places, so what the settle skips and what the refusal calls another device can not drift apart. Compare with sameDeviceIdentity. The record help said only closing the session disposes what a failed record stop keeps. That holds only while the daemon holds the session; once it does not, the next record start on that device settles it. Co-Authored-By: Claude Opus 5.5 <[email protected]> --------- Co-authored-by: Claude Opus 5.5 <[email protected]>
…llstack#4182) * refactor(apple): share the snapshot bridge reader with the runner build Split request validation out of the guest bridge server into SnapshotBridgeRequest.m and name the reader translation units once (SHARED_SNAPSHOT_BRIDGE_READER_FILENAMES). The guest bridge compiles them with its socket server; the runner fingerprint and its CI cache key now cover them, because the runner compiles the same reader. A host that serves the reader for other work can turn off the watchdog that ends the guest process (SNAPSHOT_BRIDGE_WATCHDOG_ENDS_PROCESS). * refactor(apple): separate the bridge source pipeline from its transport createSnapshotBridgeSource owns bounds, depth hints, acquisition, and the recovery record; a transport supplies only how one accepted envelope arrives. The local guest process is the first transport. acceptSnapshotBridgeEnvelope is the one acceptance rule for a bridge reply. * feat(apple): serve the Simulator AX bridge from the XCTest runner A provider iOS Simulator, such as Limrun, cannot run the guest bridge process: its simctl spawn is restricted and it forwards no socket. The runner now compiles the same reader and answers the snapshotBridge command off its journal and off the XCTest main thread. It abandons a read that outlives its budget instead of ending itself. The Apple runner runtime routes captures through the shared snapshot route with a runner channel, so a provider Simulator gets the local tree, recovery, depth cut, producer, generation circuit, and typed XCTest fallback. The runner names the app generation itself (target) and refuses another process (stale_target). The route refreshes a cached target before it trusts an open circuit, so a relaunch re-enables the bridge; a failed refresh keeps the cached generation. Only the runner's foreground read can prove that no system surface covers a provider Simulator, so that read starts a runner that is not up yet, and the host-side reads now go out together. Runner commands answered off the queue carry an explicit queued: false trait, so the bridge read is never charged as queued work. * fix(limrun): require an Xcode 26 runner that embeds XCTest Limrun simulators run iOS 26. An Xcode 27 simulator build embeds no XCTest frameworks and resolves them from the build machine, so it cannot start on a remote simulator. The release workflow now builds with Xcode 26, the package script refuses a runner without XCTest.framework, and the Limrun runner products check refuses such a runner before upload. * fix(apple): fence the provider surface proof to one runner session Co-Authored-By: Claude <[email protected]> * refactor(snapshot): remove the dead limrun-ios-tree producer Co-Authored-By: Claude <[email protected]> * test(apple): guard the snapshot bridge refusal codes against emitter drift Co-Authored-By: Claude <[email protected]> * docs(adr): record that observe-only captures bypass the bridge route Co-Authored-By: Claude <[email protected]> * fix(apple): give a tree that changed during a bridge read capture scope The bridge reader re-reads a node whose children the first read withheld. When a scrolling list recycles its cells between the two reads, the re-read returns fewer children, and the runtime reported every such reader error as snapshot-tree-malformed. The route then retired the app generation, so one scroll moved every later capture of the app to XCTest (seen on Bluesky's feed). The reader errors are now a typed enum, and the runtime maps each one to its refusal: a changed tree is snapshot-tree-changed with capture scope, and a foreground change during a continuation is foreground-owner-changed. Co-Authored-By: Claude <[email protected]> * refactor(apple): prove surface absence on the runner channel as on the guest channel callstack#4172 moved the guest channel's presence check into the bridge request: when no runner can answer the foreground read, the route names the surface host executables, and the reader checks the process table in place. The runner channel now takes the same path instead of a constant 'unknown' presence probe. The runner forwards the executables to the shared reader and names the Simulator it runs in. A sandbox that refuses the process table answers system-surface-host-unverified, so the capture uses XCTest and says why. The surface-host refusal codes join the refusal fixture, and the runner request fixture pins a describe that carries the executables. Co-Authored-By: Claude <[email protected]> * fix(apple): keep a malformed continuation re-read as snapshot-tree-malformed A continuation whose children were not an array shared the tree-changed code, so its capture-scoped refusal never opened the generation circuit. Only a child-count shrink is tree-changed now. Co-Authored-By: Claude <[email protected]> * chore(apple): list the request validator among the packed bridge assets Co-Authored-By: Claude <[email protected]> * docs(apple): correct provider names, refusal codes, and the process-table rejection Co-Authored-By: Claude <[email protected]> * fix(apple): type a bridge refusal before reading the target it names Co-Authored-By: Claude <[email protected]> * fix(apple): hold a runner-served envelope to the bound with its frame header The guest bridge and the host decoder count the 4-byte length prefix against maxResponseBytes; the runner did not, so an envelope could pass the runner and fail the host. The default bound is the reader's own frame bound, and the reader request adds only the fields the host sent. Co-Authored-By: Claude <[email protected]> * refactor(apple): require describe identity and a session id by type A describe request cannot omit the identity it is checked against, and the foreground read always carries the runner session it expects. Adds the observe-only admission and runner budget regression tests. Co-Authored-By: Claude <[email protected]> * fix(apple): refuse a JSON boolean where the reader needs a number Co-Authored-By: Claude <[email protected]> * fix(limrun): bundle the Swift Testing interop library when the SDK ships it Co-Authored-By: Claude <[email protected]> * test(apple): narrow the runner bridge request union in the route tests Co-Authored-By: Claude <[email protected]> * test(apple): run the shared reader tests on the iOS Simulator only The runner compiles the shared reader for the iOS Simulator only, so the macOS runner unit-test build could not link SnapshotBridgeHandleRequest. Co-Authored-By: Claude <[email protected]> * perf(apple): settle the surface foreground proof in registry order The host reads still go out together, but a decisive first host now answers without waiting for the reads after it. Co-Authored-By: Claude <[email protected]> * fix(apple): let the off-simulator runner carry its own bridge refusal Off the iOS Simulator the default envelope bound was 0, so the runner's runner-bridge-unavailable refusal came back as response-too-large when a request named no bound. Co-Authored-By: Claude <[email protected]> * fix(ios): preserve runner bridge refusal evidence * test(apple): match the native bridge version macros exactly The wire-vocabulary guard matched SNAPSHOT_BRIDGE_PROTOCOL_VERSION 1 as a substring, so a native protocol version of 10 still passed. It now matches each whole #define line. Co-Authored-By: Claude Opus 5.5 <[email protected]> * refactor(apple): name a missing Simulator by leaving its key out The runner sent an absent SIMULATOR_UDID as an empty string, and its bridge handler re-detected that with a second copy of the reader's 64-character UDID bound. The request builder is the only place that knows the UDID is absent, so it now leaves simulatorUdid out, and the handler refuses the surface proof with system-surface-host-unverified when the key is missing. The reader keeps the only UDID validation. Co-Authored-By: Claude Opus 5.5 <[email protected]> --------- Co-authored-by: Claude <[email protected]>
muvaf
force-pushed
the
codex/limrun-sync-install
branch
from
November 5, 2026 20:10
bafbb75 to
12c2b67
Compare
muvaf
force-pushed
the
codex/limrun-sync-install
branch
from
November 5, 2026 20:59
12c2b67 to
d2c9b9f
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Upstream review: callstack#4225. This fork PR preserves the isolated layer for review.
Summary
Use Limrun app sync for local iOS and Android install/reinstall, including changed binaries. The package now bundles the real delta encoder and its license notices. One session-owned basis cache replaces per-materialization caches; SDK work retains its source until completion. Install-only sync leaves apps stopped, and plain SDK failures receive typed errors. Asset URL installation remains supported.
This follows native XCTest replacement #4224; its own 38-file change uses released SDK 0.69.1. Scope includes replacing an event-loop polling limit in an Apple deadline test after CI mutation jobs exposed the race. GitHub requires this upstream PR to target
main, so review the isolated sync diff. Merge callstack#4224 first, then rebase. CLI syntax is unchanged:agent-device install ./MyApp.apporagent-device install ./app.apkin a Limrun session.Validation
The full affected gate passes at
8f01e5cd7. Headbafbb7556adds only test cleanup and the deterministic deadline fixture;pnpm check:affected --run --base 8f01e5cd7 --head HEADalso passes. Both receipts haveexactCommit: true. The linked mutation evidence covers the fixture change; CI reruns remain authoritative. Receipts, independent review and live evidence compare basea95049b9a, which fails changed-binary installs on both platforms, with the built final runtime. Remote rebuilds, stopped-state checks and installed-content checks pass; captured uploads show 5.7 KB iOS and 1.6 KB Android deltas. The earlier live run also verifies data removal and uninstall. Required GitHub checks remain CI obligations.