Skip to content

v1.22.0

Choose a tag to compare

@janicduplessis janicduplessis released this 18 Oct 05:29
· 7 commits to main since this release
ea8e64c

Stim Desktop and the Stim phone app ship through their own workflows; this release covers the twelve npm packages (@stim-cli/core, @stim-cli/cache, @stim-cli/cache-s3, @stim-cli/agent-driver, @stim-cli/agent-driver-agent-device, @stim-cli/metro, @stim-cli/expo-build-cache, stim, @stim-cli/ci, @stim-cli/transport, @stim-cli/transport-tailscale and @stim-cli/server). No package publishes for the first time in this release. Commits that only changed those apps, tests, CI or internal structure are not listed.

Removed (breaking)

  • Custom Expo buildCacheProvider plugin modules are no longer supported. A project whose expo.buildCacheProvider names a custom module now gets an unsupported-provider note ("Custom Expo buildCacheProvider plugins are not supported. Use EAS or a Stim cache provider.") in --plan output and in ios and android build progress (provider not usable: ...), and falls back as if no provider were configured; the module is never loaded or run. expo.buildCacheProvider: "eas" (EAS CLI caching), the local Expo cache adapter and @stim-cli/cache providers, including S3, are unchanged. If you use a custom plugin, switch to EAS or a Stim cache provider. (stim) 94bb19775

New

  • Completed builds on paired workers are reused instead of rebuilt. After a local and shared-cache miss, stim asks the paired worker for a completed build of the same inputs before placing a build, including when the worker is busy or the caller is idle. A hit skips source upload and compilation; a missing or corrupt archive follows normal placement, and older workers keep their normal build path. Each kind is negotiated by its own server feature and uses the existing authenticated, repository-scoped file cache. (stim, @stim-cli/server, @stim-cli/core)
    • Native Gradle: the worker caches APKs under its own complete inputs (worker toolchain, signing files and environment values) when the build transfers a complete android.artifactInputs declaration, and the caller can fetch a completed APK with feature native-gradle-peer-cache. Policy flows through native-gradle-artifact-cache. The returned fingerprint stays null, and worker APKs never enter the caller-key cache. 0bac453be, 4fcf220e9
    • React Native and Expo development builds: exact iOS Simulator and Android archive lookup with feature react-native-peer-cache, matching the post-mutation native fingerprint, normalized build choices and a compatible toolchain. Release and physical-device builds keep their existing paths. ac95a793a
    • SwiftPM macOS apps: reuse of a worker's completed app with feature swiftpm-peer-cache, for apps that declare complete, repeatable inputs through macos.artifactInputs. d87f39cab
  • Completed SwiftPM macOS apps are reused across worktrees. macos.artifactInputs (project setting, object) with complete: true declares pinned inputs that produce a relocatable app, with ignored, outputs, localFiles and environment for exact ignored, generated, private or environment inputs. A hit comes from the local build cache or an optimizations.buildCache / cache.provider files provider, is verified, and is re-signed with the receiving workspace's identifier and name before placement or build-slot acquisition. Without the declaration, builds compile as before. stim macos --plan reports cacheHit for a verified local completed artifact. See stim guide macos for what the declaration must cover. bb4dfc43a
  • Build placement ranks workers by reusable dependency installs. For React Native and Expo, a worker whose existing install matches the requested project, lockfile and package-manager arguments (modules and install marker present) ranks before load. Checkout and build-folder presence, and the unverified hints of older workers, no longer affect ranking. Clients send dependencies: { project, packageName } only to servers with the new feature build-dependency-cache. Exact completed-artifact hits still come first. (stim, @stim-cli/server, @stim-cli/core) 439c20847
  • Owned simulators and emulators are muted each time Stim boots them. After a successful boot, Stim sets the volume to 0 and reads it back: xcrun devicectl device settings audio --device <udid> --volume 0 for iOS simulators, and for Android emulators the media stream only, through a 1.4 KB helper shipped in the CLI (shim/android-audio.jar) that Stim pushes to /data/local/tmp, runs with app_process, removes, and confirms with cmd media_session volume --stream 3 --get; this works on Android 14, whose shell ignores the plain volume setter, and needs no host JDK. Stim prints a device phase line when the mute is confirmed and a warning, then continues, when it is not. A device that is already running is not touched, so sound turned on with Stim Desktop's per-device toggle lasts until that device next boots. Hosted (device-host) boots are unchanged. 2459e5df3, 289d8701d
  • stim-server service update --no-wait and stim-server setup --no-wait. Service updates wait for offloaded builds and hosted sessions to finish by default. --no-wait restarts with that work active: shutdown interrupts it, and shutdown verification, health checks and rollback still apply. Setup applies the flag to the service update it runs, and its remedy for a separate update carries it. (@stim-cli/server) c180eb0cf
  • In-process APIs on createStim. Each accepts an AbortSignal and drains its owned children and claims on cancellation, reporting STIM_CLAIM_UNAVAILABLE with recovery paths when cleanup cannot be verified:
    • plan({ platform: 'ios' | 'android' | 'macos', slot?, signal? }) returns the next configured native build as --plan --json reports it, without building or launching. (stim, @stim-cli/cache, @stim-cli/cache-s3) 51c5cd60b
    • start({ remote?, resetCache?, waitSeconds?, signal?, beforeStart? }) starts or reuses the managed dev server and returns flat start facts. Starting and stopping from a child folder resolve the same enclosing app. 243841b81
    • collectGarbage({ cache?, olderThan?, worktrees?, delete?, idleMs?, signal? }) runs the CLI's GC selections and actions and returns { report, exitCode }; gc() stays read-only. A deleting or idle sweep takes the home's exclusive maintenance claim and a concurrent one rejects with STIM_CLAIM_REFUSED. 690f2dc8a
    • removeWorktree({ target?, force?, signal?, beforeRemove? }) returns { removed, exitCode } with the CLI's removal checks. 28c3ac257
    • warmWorktree({ signal?, beforeWarm? }) copies missing ignored state into the linked worktree and returns { exitCode }, with a 90 minute execution and 35 second cleanup budget. It does not refresh or install dependencies. A failed copy that created a new entry now removes that partial entry after its child drains. b5ff73bf4
    • requestMachineAccess({ ticket?, signal? }) requests build and device-host access from the configured remote machines without applying project repairs; a person still approves each capability on the worker. It returns the iOS doctor report. 757f90b40
    • run() exposes the existing remote selector for native runs and forwards it for macOS, so a macOS request with remote: '<machine>' no longer takes the local launch path. The macOS result's launched type now includes 'unverified'. dc29525ed
  • New private Desktop stim-server methods and features. Each is available only to the Desktop connection that proves the bundled server's private bootstrap key (for the same Stim home); phones, other local clients and adopted servers are refused. Each subscription streams desktop-*-progress and desktop-*-ended events; desktop.plan.get returns the plan in its reply. Each runs the in-process API above, waits for cleanup on cancellation and never replays on reconnect. (@stim-cli/server, @stim-cli/core)
    • desktop.run.subscribe (feature desktop-run): iOS, Android, macOS and web runs, with a one-hour default deadline and a 1 MiB output limit. c80c8cf89
    • desktop.build.subscribe (desktop-build): artifact-only builds for iOS, Android and macOS. c5393e953
    • desktop.start.subscribe (desktop-start): dev-server startup. 243841b81
    • desktop.plan.get and desktop.plan.cancel (desktop-plan): next-build reports for a selected existing folder. The registered-workspace build.plan method for phones now also runs in process. 89034c015, e5ca4426c
    • desktop.gc.subscribe (desktop-gc): GC selections and actions, preserving a failing exit status on partial failure. ac7b6952e
    • desktop.worktree.remove.subscribe (desktop-worktree-remove) and desktop.worktree.warm.subscribe (desktop-worktree-warm, 90 minute default deadline). 3a4921c14, aa73c5e6f
    • desktop.machine-access.subscribe (desktop-machine-access): { cwd, ticket? }. 757f90b40
  • Lower idle cost and faster reaction in several waits. Waits that polled now wake on file, process or write events, keeping a slower repair check for a lost event:
    • stim logs --source agent --follow and server log subscriptions follow agent state and claim directories instead of re-reading every 500 ms (repair read every ten seconds). f4b41e242
    • stim-server setup notices the requesting machine's approval from registry changes (one-second repair). a1cae8426
    • Windows service startup waits on supervisor.json changes instead of a TaskScheduler query every 250 ms (one-second repair). e170583bc
    • Hosted Metro gateway acknowledgements wake from the state file instead of a 100 ms reread (caller repair is one second after the first 500 ms). 1ab1c5442
    • Browser launch verification (stim web) reads the owned browser log by file events, and browser registration wakes from its supervisor by IPC (Windows keeps 250 ms checks). b2c2acb60, 232a123be
    • Local and hosted macOS launches (stim macos) wake from the supervisor by IPC. 83cd41dc6
    • Binary build-source uploads wake backpressure from write completion instead of a 5 ms check. 4767d52a8
    • Managed tunnel, Metro startup rollback and Windows launcher cleanup finish on child exit instead of a 25 ms delay. d1882ea04
    • @stim-cli/core oversight wakeAt includes physical-device lease expiry and unattended EAS-session age, so callers can schedule those attention changes without polling. 7ae982cde
    • Status retries pending merge verdicts without rescanning unchanged worktrees (37 to 16 git status commands in a real eight-worktree fixture). 6ec3a74ed

Fixes

  • ios --device and android --device ignore ios.remote and android.remote. With either setting at auto or a machine name, a physical-device run refused with STIM_BAD_ARG. Those settings place owned simulators and emulators, so --device no longer reads them. --device --remote local now works. --device with any other --remote still refuses, and the error now names the value passed. 23bbd7bd4
  • Build workers recognize an available iPhone by CoreSimulator's device-type identifier instead of its name, so simulators that Stim renamed are found for offers and destination selection; an iPad named "iPhone" stays excluded. (stim) 1a747b4c3
  • S3 file uploads reopen the completed file and retry up to three PUT attempts on ECONNRESET, with short cancellable delays and conditional writes preserved. (@stim-cli/cache-s3) 6c318763b
  • The supervisor stays alive and keeps its registrations and hosted gateways when the dev server's shutdown cannot be confirmed, so a later shutdown can retry. Previously a command that survived termination could outlive a supervisor that had cleared its state. 6745ce291
  • A successful browser navigation no longer leaves a timeout that delays a standalone caller's natural exit by about one second. 480879f43
  • stim status --watch on macOS stops without per-workspace delays: it shares one recursive workspace watcher, and watches CoreSimulator device.plist files instead of each device directory. A real-home probe took 17.8 seconds to exit after SIGINT before and 4.18 seconds after; a 15-device fixture's watcher disposal fell from 1.11 s to 140 ms. 1b5c264c8, e60218eb4
  • Doctor's setup command, once approved on the worker, prints the instruction to run stim doctor again on the requesting machine before stim-server setup waits. Ordinary setup and Desktop-generated commands are unchanged. (@stim-cli/server, stim) 6063465bd

Migration notes

  • Owned simulators and emulators boot muted. Turn sound on per device in Stim Desktop's options (Sound for simulators, Media sound for emulators); it lasts until that device next boots.
  • A project that relied on a custom expo.buildCacheProvider module must move to EAS ("eas") or a @stim-cli/cache provider.

QA

The maintainer authorized a stable release without the pre-tag QA gate on 2026-10-18 ("Can u do a no qa release regular release, no rc"). This deviates from RELEASE.md section 3, which has no waiver for a stable version; the expedited RC lane is for prereleases only and was not used. The waiver covers only section 3.

Omitted before the tag: all eight rows that node scripts/release-qa-matrix.mjs v1.21.0 requires (415 changed paths, 210 mapped to a row, 0 unclassified): loop (expo and bare, iOS and Android), logs (iOS, Android), caches (iOS, Android), real-repository, remote-provider, release-build (iOS, Android), android-flavor and launch-evidence (iOS, Android), plus the native suites test/e2e/native/run-native-e2e.mjs and test/e2e/native/run-cache-e2e.mjs. None of the items above was exercised by hand on this candidate, including the muted boot, worker artifact reuse and the custom Expo buildCacheProvider removal. Not run: test:compat, which needs a connected iPhone.

Run before the version commit, on the bumped candidate: format:check, lint, build, typecheck, knip, release:prep --check (12 packages at 1.22.0, 23 workspace: ranges), test:runtime, test:e2e (116 of 116), CLI --version (1.22.0), --help and guide agent, licenses:check and format:check in apps/mobile, and pnpm tarball inspection of the twelve packages (README and LICENSE in each, version 1.22.0, internal dependencies on ^1.22.0 or 1.22.0, no workspace: range). pnpm test ran 10162 tests on a Mac at load average 110 to 190: 10100 passed, 27 skipped, 35 failed in 16 files, almost all timeouts or timing assertions. Rerun alone, at load average 187, packages/server/__tests__/device-host.test.ts, packages/stim-cli/src/__tests__/report-runtime.test.ts and packages/stim-cli/src/__tests__/start-context.test.ts passed and 27 tests in the other 13 files failed again; scripts/agent-benchmark/macos-processes.test.mjs fails at its sandbox-exec call on this Mac, as in 1.21.0. The local unit result is therefore not a pass; the full unit suite is left to exact-commit CI.

Run after the version commit landed: exact-commit CI on main (Node 22 and Node 24 jobs and published runtime (node 22.12.0)), and the Release workflow's own gates. The full Windows unit job of a dispatched CI run on the candidate is recorded, not gated, per #3878.