stim 1.24.0 → 1.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. package/README.md +1 -1
  2. package/dist/{acquire-artifact-CEs02U2o.mjs → acquire-artifact-DVsvSwf7.mjs} +1 -1
  3. package/dist/{act-Cyvm8xYc.mjs → act-C-BqJEpu.mjs} +158 -36
  4. package/dist/{activity-Dq3ZcdD8.mjs → activity-Lnb_EoRY.mjs} +4 -5
  5. package/dist/{agent-device-usage-output-CccomeE0.mjs → agent-device-usage-output-DbXy5tg8.mjs} +4 -4
  6. package/dist/{android-CX5IcRuf.mjs → android-B5P81yI-.mjs} +1 -1
  7. package/dist/{android-DcztblCT.mjs → android-CSbLRD53.mjs} +5 -5
  8. package/dist/{android-BZppyPe_.mjs → android-CiNQw3kq.mjs} +81 -52
  9. package/dist/{android-882m4hxX.mjs → android-DTAcRwBC.mjs} +81 -34
  10. package/dist/{android-cas-BtCOrNqb.mjs → android-cas-BTVhZ0Qs.mjs} +4 -4
  11. package/dist/{android-project-DVWyCbWU.d.mts → android-project-DUGMbq28.d.mts} +61 -3
  12. package/dist/api.d.mts +8 -5
  13. package/dist/api.mjs +27 -27
  14. package/dist/apk-signed-tail-B46U9hXc.mjs +142 -0
  15. package/dist/{artifact-BR5VNArK.mjs → artifact-BxREhAaV.mjs} +25 -19
  16. package/dist/{artifact-Bdt_n9Jc.mjs → artifact-CsAP6AGK.mjs} +36 -21
  17. package/dist/{attempt-DW-mQqM7.mjs → attempt-ClyJlZPT.mjs} +4 -2
  18. package/dist/{browser-web-ycg9qWB8.mjs → browser-web-rYPyqhTf.mjs} +11 -12
  19. package/dist/{budget-dPGuhNai.mjs → budget-k2mZGvvk.mjs} +242 -38
  20. package/dist/{build-CaF574eS.mjs → build-DPhApsjV.mjs} +28 -24
  21. package/dist/{build-cache-HFUQ9eLz.mjs → build-cache-DrXNCy3T.mjs} +123 -23
  22. package/dist/{build-plan-JsDUq4w1.mjs → build-plan-DrMMdayj.mjs} +5 -5
  23. package/dist/{cache-manifest-BPXRONJz.mjs → cache-manifest-BEZ9Bpl1.mjs} +1 -1
  24. package/dist/cache-manifest.mjs +1 -1
  25. package/dist/{ccache-CCn1S6qG.mjs → ccache-Bx5TbxwG.mjs} +6 -6
  26. package/dist/{cli-xqAidP7O.mjs → cli-BBKFGOFl.mjs} +19 -19
  27. package/dist/cli.mjs +1 -1
  28. package/dist/{client-18-ZowoT.mjs → client-D9CaRks-.mjs} +55 -71
  29. package/dist/collector-run.mjs +4 -4
  30. package/dist/{command-output-DxC0k2rk.mjs → command-output-BF5AR2dL.mjs} +1 -1
  31. package/dist/{config-BZhXY15H.mjs → config-BktkY_zC.mjs} +321 -15
  32. package/dist/{launch-BVlyQw3T.mjs → dependencies-Dw6wfQdv.mjs} +259 -256
  33. package/dist/{dependency-state-C56ll9hY.mjs → dependency-state-DFB41vfr.mjs} +1 -1
  34. package/dist/{dev-client-w_fok0Ek.mjs → dev-client-DXR_tPVY.mjs} +2 -2
  35. package/dist/{device-BktRGdjS.mjs → device-BK-zwyd_.mjs} +64 -33
  36. package/dist/{device-4RUugw5X.mjs → device-BcYeHSFu.mjs} +14 -14
  37. package/dist/{device-capacity-Ic-kdmEV.mjs → device-capacity-DCSvUF0l.mjs} +10 -17
  38. package/dist/device-host-worker.mjs +11 -13
  39. package/dist/{device-ios-DzpSlZyd.mjs → device-ios-BLGfXQeK.mjs} +68 -25
  40. package/dist/{device-lease-run-DUtkbqBQ.mjs → device-lease-run-BmvIl7bI.mjs} +5 -5
  41. package/dist/{device-pool-CgBHk401.mjs → device-pool-Dluix4H3.mjs} +3 -3
  42. package/dist/{doctor-DZgpqR6J.mjs → doctor-Bhg1ii70.mjs} +14 -14
  43. package/dist/{doctor-BZ6oxNum.mjs → doctor-Cp-BhzTS.mjs} +1 -1
  44. package/dist/{doctor-C33AzVfR.mjs → doctor-OnwP94ne.mjs} +81 -26
  45. package/dist/{doctor-fingerprint-zlujA1Gs.mjs → doctor-fingerprint-D3a04Mhu.mjs} +8 -2
  46. package/dist/doctor-fingerprint.mjs +1 -1
  47. package/dist/{doctor-watchman-B453A231.mjs → doctor-watchman-BStK05nt.mjs} +4 -4
  48. package/dist/{eas-build-CxKUNHsb.mjs → eas-build-Dsj93iM9.mjs} +2 -2
  49. package/dist/{eas-fallback-LMxk9Kw0.mjs → eas-fallback-FwkeqqGi.mjs} +13 -12
  50. package/dist/{eas-session-ledger-Ck6YXPAO.mjs → eas-session-ledger-B5vygTiD.mjs} +5 -5
  51. package/dist/{error-diagnostics-BkKCZq4P.mjs → error-diagnostics-BOOm9Db7.mjs} +8 -8
  52. package/dist/{errors-ccaPc4ox.mjs → errors-DHm_U5gv.mjs} +2 -1
  53. package/dist/{gc-DDWEAj0w.mjs → gc-C_79zvz4.mjs} +2 -2
  54. package/dist/{gc-yVKpAOKM.mjs → gc-ldgr-hXU.mjs} +74 -43
  55. package/dist/{guide-Bggftwc5.mjs → guide-D7K0X6KG.mjs} +374 -78
  56. package/dist/{hosted-android-D4dweBof.mjs → hosted-android-Bk54ej6n.mjs} +3 -3
  57. package/dist/{hosted-client-BBTVe3X6.mjs → hosted-client-fHO8yqzc.mjs} +5 -5
  58. package/dist/{hosted-ios-Sn0djzg8.mjs → hosted-ios-D55QhYaD.mjs} +1 -1
  59. package/dist/{hosted-logs-CYIZ5ubJ.mjs → hosted-logs-BfbmDM4M.mjs} +17 -11
  60. package/dist/hosted-logs-sync-B0pvmOk-.mjs +195 -0
  61. package/dist/{hosted-macos-CXjsUNBR.mjs → hosted-macos-CPl0OZ9h.mjs} +5 -5
  62. package/dist/{hosted-native-Doxbfzem.mjs → hosted-native-CpDB3-PO.mjs} +6 -7
  63. package/dist/{idle-shutdown-BInjLKqm.mjs → idle-shutdown-FfxL1KdG.mjs} +22 -9
  64. package/dist/{ios-BItP6lkn.mjs → ios-B7-lToDu.mjs} +8 -8
  65. package/dist/{ios-CVUFFZ6C.mjs → ios-WxpDJ5T1.mjs} +129 -14
  66. package/dist/{ios-device-BI38NFFH.mjs → ios-device-CsyryvpT.mjs} +1 -1
  67. package/dist/{ios-B1FXRCMX.mjs → ios-ftxdQbJS.mjs} +84 -51
  68. package/dist/{ios-project-KYqeJURj.mjs → ios-project-Ty5pHl9O.mjs} +6 -6
  69. package/dist/{ios-state-DlElquYD.mjs → ios-state-Cw7-WQf2.mjs} +1 -1
  70. package/dist/{js-swap-BvrQvXHY.mjs → js-swap-BMhXYVDS.mjs} +4 -4
  71. package/dist/{launch-SfzGH_s9.mjs → launch-DOp8RjDz.mjs} +32 -27
  72. package/dist/{logs-DXE32kyb.mjs → logs-dQDki6Tt.mjs} +9 -173
  73. package/dist/{logs-ovlU7m7N.mjs → logs-s1dRIG1E.mjs} +85 -36
  74. package/dist/{macos-B3BveDrU.mjs → macos-B0VEV3Gf.mjs} +1 -1
  75. package/dist/{macos-CCfhb-jT.mjs → macos-DBBPNnIA.mjs} +18 -19
  76. package/dist/macos-run.mjs +1 -1
  77. package/dist/maintenance-run.mjs +28 -11
  78. package/dist/{metro-Bu8LBfzS.mjs → metro-CjC10t6E.mjs} +2 -2
  79. package/dist/{metro-gateway-DtsOy-0P.mjs → metro-gateway-B6_PgD0L.mjs} +2 -3
  80. package/dist/{named-ports-BWDuU5oQ.mjs → named-ports-BG0j1X4g.mjs} +2 -2
  81. package/dist/{native-android-96BBizbq.mjs → native-android-CL2NGiik.mjs} +17 -17
  82. package/dist/{native-gradle-artifact-inputs-BcLYBZil.mjs → native-gradle-artifact-inputs-btSUulme.mjs} +3 -3
  83. package/dist/{native-run-BmH7U8pr.mjs → native-run-BfIDFKLH.mjs} +2 -3
  84. package/dist/{native-runtime-DgtJkzIa.mjs → native-runtime-Dq3RBcON.mjs} +6 -7
  85. package/dist/{native-xcode-inputs-irplbQb0.mjs → native-xcode-inputs-BW7H8JGu.mjs} +2 -2
  86. package/dist/{native-xcode-ios-BY-6hh6M.mjs → native-xcode-ios-CBXJAxfC.mjs} +18 -18
  87. package/dist/offload-worker.d.mts +7 -2
  88. package/dist/offload-worker.mjs +34 -23
  89. package/dist/{ownership-DxlZGDcV.mjs → ownership-BjYvMNX_.mjs} +2 -2
  90. package/dist/{phone-reverses-BdsdesVF.mjs → phone-reverses-lmj6kcPm.mjs} +3 -3
  91. package/dist/{placement-log-DUvqqsB-.mjs → placement-log-DIFPQAXp.mjs} +1 -1
  92. package/dist/{plan-placement-4axc0Hs7.mjs → plan-placement-IiiSQhtz.mjs} +3 -3
  93. package/dist/{ports-D-E_OiAO.mjs → ports-C2_w1Z0Z.mjs} +3 -3
  94. package/dist/prebuild-gmouvmJZ.mjs +1247 -0
  95. package/dist/{project-Dz-hWkb-.mjs → project-BUu6Is4k.mjs} +15 -16
  96. package/dist/{react-native-android-DOinxgwB.mjs → react-native-android-Cgg99REN.mjs} +48 -33
  97. package/dist/{react-native-build-AuCu-aAP.mjs → react-native-build-oNwqF5Wb.mjs} +8 -8
  98. package/dist/{react-native-doctor-2ivJUmwZ.mjs → react-native-doctor-BhA3KUte.mjs} +49 -8
  99. package/dist/{react-native-ios-D5zTIuH8.mjs → react-native-ios-B-nf4rjN.mjs} +40 -25
  100. package/dist/{recordings-CdPwYwvs.mjs → recordings-BldzVKq0.mjs} +1 -1
  101. package/dist/{reload-Ck9RJeSq.mjs → reload-C9OduiNc.mjs} +1 -1
  102. package/dist/{reload-C1TTVesC.mjs → reload-CCR7KC_l.mjs} +14 -14
  103. package/dist/{remote-DEZ7AOVq.mjs → remote-C8S7FlPF.mjs} +16 -7
  104. package/dist/{result-DqBrb12U.mjs → result-7F5hEwQJ.mjs} +17 -17
  105. package/dist/{run-BIwoU44q.mjs → run-BqB0v3pt.mjs} +1 -1
  106. package/dist/{selection-BLtvaqxc.mjs → selection-BNPBZ6T5.mjs} +157 -44
  107. package/dist/{server-bare-Z1ToVLPn.mjs → server-bare-A75rDIdD.mjs} +55 -3
  108. package/dist/{server-command-C0pmjNdG.mjs → server-command-CssKFOD7.mjs} +4 -4
  109. package/dist/{server-expo-Tt_hLRpy.mjs → server-expo-eQfOdydb.mjs} +8 -6
  110. package/dist/server-expo-wvcj5tiZ.mjs +2 -0
  111. package/dist/{settings-RKwY33wv.mjs → settings-CosW5X-S.mjs} +59 -61
  112. package/dist/{settings-kkt0vP4w.mjs → settings-OaGQF-ri.mjs} +1 -1
  113. package/dist/{settings-write--gj6MACV.mjs → settings-write-QjYfSrjF.mjs} +5 -5
  114. package/dist/settings.schema.json +71 -6
  115. package/dist/{simslim-CdWKT3vA.mjs → simslim-Bax1qcxu.mjs} +22 -6
  116. package/dist/{slot-launch-A1BgotaM.mjs → slot-launch-CYCEhfBY.mjs} +6 -6
  117. package/dist/{stage-ERTMh88n.mjs → stage-jKQ7CP_B.mjs} +1 -1
  118. package/dist/{start-Bmg_qmHi.mjs → start-C_wefNKB.mjs} +1 -1
  119. package/dist/{start-BKGrEhzG.mjs → start-_BdJ32cZ.mjs} +9 -10
  120. package/dist/{state-B1ig0Kqy.mjs → state-C1i2nMOP.mjs} +2 -3
  121. package/dist/{state-BfAltYJP.mjs → state-C4OdpOTF.mjs} +1 -2
  122. package/dist/{stats-B-ckuiUg.mjs → stats-tEpEEH-u.mjs} +4 -4
  123. package/dist/{status-Pw-807f0.mjs → status-B9M7x4CZ.mjs} +1 -1
  124. package/dist/{status-Fect2w9R.mjs → status-CRXz-Gtj.mjs} +1 -1
  125. package/dist/{status-DfFp6a76.mjs → status-DQrqodLj.mjs} +22 -23
  126. package/dist/{status-watch-XC7F-AMf.mjs → status-watch-B5wDIH_o.mjs} +4 -4
  127. package/dist/status-worker.mjs +1 -1
  128. package/dist/{stim-desktop-DS0mzR8U.mjs → stim-desktop-6Dqp-gjX.mjs} +1 -1
  129. package/dist/{stop-BFu7X_Do.mjs → stop-CmdNhGlC.mjs} +2 -2
  130. package/dist/{stop-CL1kDHds.mjs → stop-DRqQVqDJ.mjs} +74 -53
  131. package/dist/{stop-DhclRwOO.mjs → stop-ySNQHXMG.mjs} +4 -5
  132. package/dist/supervisor-run.d.mts +1 -0
  133. package/dist/supervisor-run.mjs +15 -16
  134. package/dist/{support-B3tzVhKn.mjs → support-BGSCBoib.mjs} +4 -4
  135. package/dist/{support-B1W-hmC2.mjs → support-CVOCp0Ft.mjs} +15 -12
  136. package/dist/{swiftpm-artifact-inputs-BWYPj9Sn.mjs → swiftpm-artifact-inputs-CfUzt_0Z.mjs} +1 -1
  137. package/dist/{swiftpm-macos-BVCP5mO4.mjs → swiftpm-macos-x6HPpl_2.mjs} +10 -10
  138. package/dist/toolchain-DoavrvIA.mjs +2 -0
  139. package/dist/toolchain-J4J8MrAr.mjs +2107 -0
  140. package/dist/trigger-DzJu208Q.mjs +2 -0
  141. package/dist/{trigger-CBMe2YLH.mjs → trigger-KVaWWNyn.mjs} +1 -1
  142. package/dist/{warm-progress-ol0ZypEu.mjs → warm-progress-DvhTX_eu.mjs} +3 -4
  143. package/dist/web-BFLueSh-.mjs +2 -0
  144. package/dist/{web-BH7peScT.mjs → web-Bihbf7-u.mjs} +8 -9
  145. package/dist/web-run.mjs +4 -4
  146. package/dist/{workspace-state-CeBpIZgr.mjs → workspace-state-pUg3_RRA.mjs} +45 -7
  147. package/dist/{workspaces-CSV2Orf4.mjs → workspaces-g2lhdBXF.mjs} +17 -17
  148. package/dist/{worktree-Dw8kfacb.mjs → worktree-BKuklFh_.mjs} +34 -31
  149. package/dist/{worktree-DEDUzO_L.mjs → worktree-Bm4cb-nU.mjs} +1 -1
  150. package/package.json +8 -8
  151. package/shim/bundle-response.cjs +53 -1
  152. package/shim/bundle-response.d.cts +1 -1
  153. package/shim/expo-metro-config.cjs +23 -1
  154. package/dist/created-devices-D2Aca-xu.mjs +0 -52
  155. package/dist/paths-DQtTgsBf.mjs +0 -42
  156. package/dist/prebuild-DmVCf5xb.mjs +0 -501
  157. package/dist/server-expo-CEntXzRw.mjs +0 -2
  158. package/dist/toolchain-BjgKdoEf.mjs +0 -2
  159. package/dist/toolchain-D3bmRMia.mjs +0 -897
  160. package/dist/trigger-BLsHgAGR.mjs +0 -2
  161. package/dist/web-BdOFlTJr.mjs +0 -2
  162. package/dist/xcode-DHu41qQ4.mjs +0 -910
@@ -1,8 +1,8 @@
1
- import { t as ANDROID_AVD_CONFIG_HELP } from "./settings-RKwY33wv.mjs";
2
- import { i as findProjectRoot$1 } from "./project-Dz-hWkb-.mjs";
3
- import { h as RECENT_LAUNCH_MS } from "./eas-session-ledger-Ck6YXPAO.mjs";
4
- import { o as guideStatus, t as WATCHMAN_NESTED_WORKTREES } from "./doctor-watchman-B453A231.mjs";
5
- import "./status-watch-XC7F-AMf.mjs";
1
+ import { t as ANDROID_AVD_CONFIG_HELP } from "./settings-CosW5X-S.mjs";
2
+ import { i as findProjectRoot$1 } from "./project-BUu6Is4k.mjs";
3
+ import { h as RECENT_LAUNCH_MS } from "./eas-session-ledger-B5vygTiD.mjs";
4
+ import { o as guideStatus, t as WATCHMAN_NESTED_WORKTREES } from "./doctor-watchman-BStK05nt.mjs";
5
+ import "./status-watch-B5wDIH_o.mjs";
6
6
  import { SETTINGS, SETTINGS_SCHEMA_URL, TUTORIAL_VERSION } from "@stim-cli/core/state";
7
7
  import chalk from "chalk";
8
8
  //#region src/guide/agent.ts
@@ -188,6 +188,11 @@ RULES DURING THE LOOP
188
188
  you do other work.
189
189
  - Run ios or android again after a native input changes. A JavaScript-only
190
190
  change does not need one.
191
+ - The build cache counts an installed package by its name and version, so it
192
+ cannot see a file you edit inside node_modules/. Change a dependency's native
193
+ code with a patch (patch-package or pnpm patch), which is hashed, or run ios or
194
+ android with --no-build-cache --remote-build local to compile it here. See
195
+ guide lifecycle builds.
191
196
  - For stale Metro transforms or file-map state, use stim start --reset-cache.
192
197
  It restarts only this app's verified owned Metro, preserving devices and other
193
198
  apps' caches. See guide lifecycle for reset scope.
@@ -395,7 +400,7 @@ FULL TOPIC LIST
395
400
  stim guide errors fallbacks # swap, cache, and install notes on a release cache hit
396
401
  stim guide lifecycle # the ordered flow, consent rules, and capacity
397
402
  stim guide lifecycle verification # reproduce, edit, verify the UI, and retain proof
398
- stim guide lifecycle readiness # add optional app readiness logs; no package required
403
+ stim guide lifecycle readiness # add recommended app readiness logs; no package required
399
404
  stim guide lifecycle builds # build optimizations, optional cache warm-up, fingerprints
400
405
  stim guide lifecycle concurrency # shared builds, wait timeouts, capacity limits
401
406
  stim guide lifecycle budget # disk and memory budgets, automatic reclaim
@@ -990,8 +995,10 @@ leased until <time>" for each one.`,
990
995
  true Metro finished the bundle response, or an app
991
996
  without Metro (a release build, or a native
992
997
  Xcode or Gradle app) has a live process; then
993
- the app stayed alive through a three-second
994
- stability window.
998
+ the app stayed alive until its JavaScript
999
+ reported \`Running "<name>"\` (plus half a
1000
+ second), or through a three-second stability
1001
+ window when it did not.
995
1002
  The command checks process liveness when the
996
1003
  platform exposes it. Errors from that window
997
1004
  are printed even when the app stays alive,
@@ -1003,12 +1010,14 @@ leased until <time>" for each one.`,
1003
1010
  bundle and the process, never a frame, and a
1004
1011
  cold app can keep rendering for a minute or
1005
1012
  more after this, which is why the stderr line
1006
- reads \`bundle loaded, process alive, stable
1007
- for 3s -- the first screen may still be
1008
- rendering\`. Poll the UI before you trust a
1009
- screenshot. Optional app-declared readiness
1010
- adds a separate stderr readiness phase; it
1011
- does not change this field. See
1013
+ reads \`bundle loaded, process alive, app
1014
+ JavaScript running -- the first screen may
1015
+ still be rendering\` (\`stable for 3s\` when
1016
+ no \`Running\` log arrived). Poll the UI
1017
+ before you trust a screenshot. Recommended
1018
+ app-declared readiness adds a separate
1019
+ stderr readiness phase; it does not change
1020
+ this field. See
1012
1021
  \`guide lifecycle readiness\`
1013
1022
  "bundling" the request DID arrive and Metro was still
1014
1023
  building or delivering when the bundle timeout closed.
@@ -1020,6 +1029,15 @@ leased until <time>" for each one.`,
1020
1029
  finishing
1021
1030
  "unverified" nothing was observed at all: usually a
1022
1031
  dev-client server picker awaiting a tap
1032
+ The wait for the app's bundle request is 20 seconds. On
1033
+ Expo, the dev client's manifest request (recorded in the
1034
+ metro log as manifest_request_started, _finished or _failed)
1035
+ counts as progress: the wait stays open while one is in
1036
+ flight and for 15 seconds after one was served, up to 60
1037
+ seconds after the launch, on any target (a new or adopted
1038
+ emulator already waits 60). A manifest is never launch
1039
+ evidence, so a launch that makes no bundle request is still
1040
+ "unverified". Bare React Native makes no manifest request.
1023
1041
  Neither "bundling" nor "unverified" is reported for an app
1024
1042
  whose process is gone when the bundle timeout closes: the
1025
1043
  run fails as FATAL, "the app process exited", with the
@@ -1243,13 +1261,27 @@ leased until <time>" for each one.`,
1243
1261
  pins Node (bin, version and match are null when no install
1244
1262
  fits the pin; match is "exact", or "major" for the newest
1245
1263
  install of the pinned major not older than the pin), else null
1246
- remoteMachines one { machine, state, dnsName?, deviceId?, requestedAt?,
1264
+ remoteMachines one { machine, state, diagnosis?, dnsName?, deviceId?, requestedAt?,
1247
1265
  expiresAt?, offloadable?, reasons?, problems?, capacity?, host? } per
1248
1266
  remote.machines entry, for its build approval; state is "approved", "pending", "not-asked",
1249
1267
  "revoked" (revoked or denied), "lapsed" (the request lapsed
1250
1268
  before approval), "key-changed", "outdated",
1251
1269
  "not-on-tailnet", "tailscale-off", "unreachable" or
1252
- "invalid". host has the same shape and presence rule as in
1270
+ "invalid". diagnosis is { layer, code, title, message, remedy }
1271
+ for the first layer that stopped this machine, present for
1272
+ "tailscale-off", "not-on-tailnet", "outdated" and "unreachable"
1273
+ (deviceHosts carries it too); the codes are
1274
+ tailscale-stopped, tailscale-logged-out, tailscale-unavailable
1275
+ (this machine's Tailscale), peer-not-found, peer-ambiguous,
1276
+ peer-offline (with last seen), peer-no-answer (the connection
1277
+ got no reply and tailscale ping fails), route-missing (nothing accepts
1278
+ connections on the serve port), route-tls, route-http-error
1279
+ (any HTTP status but 502 and 503), server-not-running (502:
1280
+ nothing behind the route), server-not-ready (503: running but
1281
+ not serving; its message gives the cause the server reports),
1282
+ server-no-answer (silent route while tailscale ping reaches the Mac), server-outdated and connect-failed (none of the above).
1283
+ Tailscale's own "offline" flag lags a reachable peer, so an
1284
+ answer from the machine outranks it. host has the same shape and presence rule as in
1253
1285
  deviceHosts, as do expiresAt (a pending request's lapse
1254
1286
  time) and "lapsed" (--fix asks again). An approved machine also carries offloadable,
1255
1287
  true when it would take this app's builds now (iOS
@@ -1265,7 +1297,16 @@ leased until <time>" for each one.`,
1265
1297
  checkout, stim-build, arch, xcode, simulator-sdk, macos-sdk,
1266
1298
  cocoapods, bundler, runtime, jdk, android-sdk, ndk,
1267
1299
  build-tools, compile-sdk, disk, busy or platform). problems lists the same
1268
- reasons as { code, reason } with that code. capacity
1300
+ reasons as { code, reason } with that code; an ndk, build-tools,
1301
+ compile-sdk, jdk, xcode or runtime problem can also carry commands and a
1302
+ lead, the lines to run on that machine (sdkmanager, the JDK install,
1303
+ xcode-select, xcodebuild or xcodes) and what they do, or followsFrom, the
1304
+ code of the problem whose fix also clears it. notes lists what a build there does without stopping:
1305
+ { code, reason, commands, lead } for an ndk, build-tools or compile-sdk package its
1306
+ Android SDK lacks and Gradle downloads on the first build (licenses
1307
+ accepted there, no android.builder.sdkDownload=false in the app), with the sdkmanager
1308
+ command that installs it ahead of time; each
1309
+ also a build-machine-<code> finding at level note. capacity
1269
1310
  is the machine's offer: { running, max, diskFreeBytes,
1270
1311
  minDiskFreeBytes, cpus?, loadPerCore?, builds?, maxBuilds?,
1271
1312
  maxLoadPerCore?, memoryUsedBytes?, memoryTotalBytes?, declined? }; an older stim-server omits the
@@ -1276,6 +1317,7 @@ leased until <time>" for each one.`,
1276
1317
  with an abnormally large footprint and, when it is safe to
1277
1318
  restart, the command
1278
1319
  offload-candidate is a note after 3+ successful local cold builds in 7 days average over 3 min, with no remote.machines and an online tailnet Mac; open Stim Desktop Settings > Remote Machines > Add
1320
+ readiness-markers-missing is a note when no JS or TS source under the app directory contains [stim:readiness]; the scan skips node_modules, native and build output directories and dot-directories, and reports nothing past 5000 source files; see stim guide lifecycle readiness
1279
1321
 
1280
1322
  ON FAILURE
1281
1323
  \`start\`, \`ios\`, \`android\` and \`web\` all print the error contract instead,
@@ -2427,6 +2469,8 @@ RULES
2427
2469
  lastPass carries startedAt, durationMs, trigger, mode, freedBytes
2428
2470
  (0 in report mode), actions, stopped (0), blocked. recent holds the last 20 action, failure
2429
2471
  and blocked NDJSON records from maintenance/maintenance.ndjson.
2472
+ What each run cleared, kept or failed is in $STIM_HOME/gc/gc.ndjson,
2473
+ read with \`stim logs --source gc --json\` (\`guide logs\`, GC LOG).
2430
2474
  invalid names invalid settings; invalid cache caps use their defaults,
2431
2475
  while invalid maintenance settings disable passes. claim contains an
2432
2476
  unresolved claim's path and reason, plus removeCommand from the claim
@@ -2753,9 +2797,16 @@ TAILNET-ONLY METRO
2753
2797
 
2754
2798
  BUNDLE WARMUP
2755
2799
  After verifying Metro, \`stim ios\` and \`stim android\` prefetch the
2756
- platform's development bundle while native work continues. Expo supplies the
2757
- entry point and bundle options through its manifest; bare React Native uses
2758
- the standard index entry and development bundle options, including lazy loading.
2800
+ platform's development bundle while native work continues. The prefetch
2801
+ requests the URL of the app's most recent bundle request on that platform,
2802
+ read from this workspace's metro log, so it matches the app's entry point
2803
+ and options and does not ask Expo for its manifest. A recorded URL whose
2804
+ entry file no longer exists under the project root or its ancestors is
2805
+ skipped. Expo computes the
2806
+ runtime version fingerprint for every manifest request. Without a recorded
2807
+ request, Expo supplies the entry point and bundle options through its
2808
+ manifest, and bare React Native uses the standard index entry and
2809
+ development bundle options, including lazy loading.
2759
2810
  Warmup is enabled by default. Set optimizations.metroWarmup=false to disable
2760
2811
  it on the next ios/android command, using the machine or project settings.
2761
2812
  For custom entry points or bundle options, set metro.warmupUrl.ios and/or
@@ -3148,6 +3199,7 @@ FLAGS
3148
3199
  shared untagged Metro and app client records (not the web
3149
3200
  page's); default also includes untagged legacy records
3150
3201
  --source <s...> metro, client, device, build, agent, maintenance, placement (one or more), or all.
3202
+ gc alone reads the machine-wide gc log (GC LOG below).
3151
3203
  An unknown value is REJECTED rather than quietly matching
3152
3204
  nothing.
3153
3205
  --level <l> minimum level: debug, info, warn, error, fatal
@@ -3324,6 +3376,27 @@ Add --errors to --source maintenance to show only maintenance_failure events;
3324
3376
  failures before a later launch marker are hidden. Machine passes are reported in
3325
3377
  status and maintenance/maintenance.ndjson; this command reads workspace logs.
3326
3378
 
3379
+ GC LOG
3380
+ stim logs --source gc reads $STIM_HOME/gc/gc.ndjson from any directory: one
3381
+ record per item that a gc run, a maintenance action or the budget gate
3382
+ cleared, kept or failed to clear. It cannot be combined with other sources,
3383
+ and --source all does not include it. --errors shows the failed items.
3384
+ Each record has src: gc, level (info, or error for a failed item), msg and
3385
+ runId the stim invocation, or the pass id of maintenance.ndjson
3386
+ trigger manual (stim gc in a terminal), desktop (STIM_DESKTOP_APP is set, so also a gc run through a stim-server Desktop started),
3387
+ maintenance, or budget (the disk, memory or workspace check before
3388
+ start, ios and android)
3389
+ via maintenance only: the command whose run started the pass
3390
+ options gc only: delete, worktrees, olderThan, cache, idleMs
3391
+ kind what the item was (the kind of gc --json results, plus devServer
3392
+ for a dev server the budget gate stops; maintenance also writes
3393
+ buildDirectory and dependencies)
3394
+ status done, kept or failed
3395
+ label, id, path, bytes, detail
3396
+ name, identifier, absolute path when the item is one, bytes freed,
3397
+ and the reason a kept or failed item was left
3398
+ Records of one run share runId. See stim guide cleanup, WHAT WAS CLEARED.
3399
+
3327
3400
  REMOTE REQUEST RECORDS
3328
3401
  A request to another machine that fails is a warn record with src: build in the
3329
3402
  run's build log: remote_connect_failed { host, capability, ms, timeoutMs, msg,
@@ -3383,7 +3456,17 @@ they stay in the local logs. These records carry no tokens.
3383
3456
 
3384
3457
  WHAT WRITES WHAT
3385
3458
  maintenance.ndjson workspace maintenance actions, failures and explaining skips
3386
- metro.ndjson the bundler, in every supervisor mode
3459
+ $STIM_HOME/gc/gc.ndjson machine-wide, not in a workspace: what gc, maintenance
3460
+ and the budget gate cleared (stim logs --source gc)
3461
+ metro.ndjson the bundler, in every supervisor mode. Stim's
3462
+ middleware adds one record per bundle request
3463
+ (bundle_response_started, _progress, _finished,
3464
+ _failed; _started carries the request's path and
3465
+ query as url, which the next warmup prefetches)
3466
+ and, on Expo, one per dev-client manifest
3467
+ request (manifest_request_started, _finished,
3468
+ _failed: debug level, with platform and requestId;
3469
+ launch verification reads them as progress)
3387
3470
  client.ndjson in-app console logs and redboxes -- BARE PROJECTS ONLY.
3388
3471
  In expo-child and command-child mode everything the
3389
3472
  dev server prints lands in metro.ndjson with raw: true,
@@ -3408,7 +3491,7 @@ WHAT WRITES WHAT
3408
3491
  The count does not attribute unknown OS errors to the app.
3409
3492
  Known JavaScript errors also print individually: Android
3410
3493
  ReactNativeJS records, and iOS com.facebook.react.log /
3411
- javascript records. These can interrupt an optional
3494
+ javascript records. These can interrupt a recommended
3412
3495
  readiness wait even without a client or Metro copy.
3413
3496
  Client and Metro errors still print individually.
3414
3497
  A native app that loaded its bundle but reported app errors
@@ -3959,7 +4042,12 @@ too old to speak protocol 2, incompatible
3959
4042
  toolchain/runtime/CPU, low disk, busy, sync/build failure, artifact failure,
3960
4043
  or a checkout that changed while it built. Unsupported build kinds also refuse.
3961
4044
  No local xcodebuild, Gradle or SwiftPM compile or alternate machine follows a
3962
- refusal. Prebuild and pod install still run on this machine before a named offload.
4045
+ refusal. For stim ios, a toolchain difference on the named machine, such as Xcode,
4046
+ the simulator runtime or the CPU, refuses before this machine runs prebuild or pod
4047
+ install, even when prebuild would have produced an app the local cache already holds, or the
4048
+ machine already holds the exact artifact. A
4049
+ missing or different global CocoaPods never refuses a named machine; a pod install
4050
+ that fails on it reports "pod install failed" with the command to run there.
3963
4051
  Unlisted or unpaired names refuse at setup before consulting the cache, without
3964
4052
  a build record or failed-run stats. A listed paired name with a cache hit needs
3965
4053
  no build and does not contact the selected machine.
@@ -5410,12 +5498,15 @@ hosting Mac keeps Metro local through a private bridge; see hosted-ios. Step 2 r
5410
5498
  Metro while other work happens.
5411
5499
 
5412
5500
  Each time Stim boots an owned local simulator or emulator, it mutes it: iOS
5413
- simulators to volume zero through Xcode's devicectl once SpringBoard has
5414
- finished starting, because SpringBoard resets the volume while it starts, and
5415
- Android 15 and later emulators to media volume zero. A device that is already
5416
- running keeps its volume. Turn sound on for one device with Sound (iOS) or
5417
- Media sound (Android) in its Stim Desktop options; the choice lasts until that
5418
- device next boots. Android 14 and older images have no shell command that can
5501
+ simulators to volume zero through Xcode's devicectl, and Android 15 and later
5502
+ emulators to media volume zero. The iOS mute runs after SimSlim has applied its
5503
+ profile and once SpringBoard has finished starting, because both a SimSlim
5504
+ reboot and SpringBoard's startup reset the volume. A device that is already
5505
+ running keeps its volume, except that a running simulator SimSlim reboots (a
5506
+ first slim, a profile change, or restoring stock services) is muted again once
5507
+ SpringBoard is back; an already slim simulator is not touched. Turn sound on
5508
+ for one device with Sound (iOS) or Media sound (Android) in its Stim Desktop
5509
+ options; the choice lasts until that device next boots. Android 14 and older images have no shell command that can
5419
5510
  set media volume, so startup warns, as it does whenever a mute is not
5420
5511
  confirmed. Host volume, Android alarm and ringtone volumes, hosted devices and
5421
5512
  physical devices are unchanged.
@@ -5503,8 +5594,9 @@ does not restart initialization. Confirm the bundle request and expected UI.
5503
5594
 
5504
5595
  Debug Android launches on an owned emulator that the run creates, or whose
5505
5596
  adoption from the pool it finishes, get up to 60 seconds for bundle loading;
5506
- the verify phase names that budget. Existing emulators, remote targets, and physical devices keep the
5507
- 20-second budget.
5597
+ the verify phase names that budget. Existing emulators, remote targets, and physical devices get 20 seconds,
5598
+ extended up to 60 while an Expo dev client's manifest request is pending or was
5599
+ just served.
5508
5600
  Bundle delivery still requires the usual stability or app readiness check;
5509
5601
  an observed fatal error ends verification without waiting for the deadline.
5510
5602
 
@@ -6289,8 +6381,8 @@ command before any EAS lookup. stim doctor reports it in a project with eas.json
6289
6381
  If another run holds the artifact claim, wait for it to finish and retry.`
6290
6382
  },
6291
6383
  readiness: {
6292
- summary: "implement optional pending/ready app logs, deadlines, errors, and platform isolation",
6293
- body: () => `OPTIONAL APP READINESS
6384
+ summary: "implement recommended pending/ready app logs, deadlines, errors, and platform isolation",
6385
+ body: () => `RECOMMENDED APP READINESS
6294
6386
 
6295
6387
  No package or SDK is needed. This applies to debug ios/android launches with
6296
6388
  Metro verification enabled, not release builds or --no-metro-check.
@@ -6330,12 +6422,18 @@ Metro verification enabled, not release builds or --no-metro-check.
6330
6422
  a missing ready message, and a startup error. Retain the relevant output.
6331
6423
 
6332
6424
  Without an observed pending, Stim keeps its default 3-second stability window
6333
- after bundle delivery. Managed Metro servers report the native bundle response
6425
+ after bundle delivery. The window ends early, half a second after the app's
6426
+ React Native runtime logs \`Running "<name>"\` for this launch on either
6427
+ platform. A fatal record or an exited process before the window ends still
6428
+ fails the launch. An error after it, such as a first render that takes longer
6429
+ than that half second to throw, appears in \`stim logs --errors\`.
6430
+ Managed Metro servers report the native bundle response
6334
6431
  finishing; build-complete output alone does not close an observed request.
6335
6432
  Without response capture, Stim falls back to the build-complete marker.
6336
6433
  When Android reports queued JavaScript loading, Stim waits for a device
6337
6434
  JavaScript log before starting stability, bounded by the bundle timeout.
6338
- Pending must be observed before the stability window closes. It opts into
6435
+ Pending must be observed before the stability window closes, early end
6436
+ included, so log it before the root component renders. It opts into
6339
6437
  waiting for ready until 30 seconds after the same completion signal.
6340
6438
  Repeated pending messages never extend the deadline. Ready can end the wait
6341
6439
  early; an app error or process exit interrupts it. No ready means readiness
@@ -6345,7 +6443,10 @@ Only exact standalone info/debug messages captured from this app's device log
6345
6443
  for this platform and current launch are accepted. Stale, other-platform,
6346
6444
  error-level, and embedded example messages are ignored. Unlabelled shared
6347
6445
  Metro output cannot identify a platform. If pending is not captured in time,
6348
- including when device logs are unavailable, the default check applies.
6446
+ including when device logs are unavailable, the default check applies. On a
6447
+ device hosted on another Mac (--remote <machine>), the device log is the copy
6448
+ pulled from that Mac during the launch check, and each window waits up to 10
6449
+ seconds for the copy to cover it.
6349
6450
 
6350
6451
  The app declares readiness; Stim does not inspect a rendered frame. This does
6351
6452
  not change launched JSON semantics or the need to inspect the expected UI and
@@ -6637,7 +6738,8 @@ result as proof instead of requiring an unrelated screenshot.`
6637
6738
 
6638
6739
  \`gc --older-than <days>\` reports and deletes, or with \`--cache parked\`
6639
6740
  erases, only the devices parked at least that long; one parked at an unknown
6640
- time stays.
6741
+ time stays. Automatic maintenance's daily age sweep deletes the devices
6742
+ parked for maintenance.olderThanDays (7; 0 never) the same way.
6641
6743
 
6642
6744
  If simulator listing or deletion fails, \`gc --delete\` reports the failure
6643
6745
  and keeps that entry. It never turns an unverified absence into a dropped
@@ -6809,11 +6911,27 @@ IOS SCHEME SELECTION
6809
6911
 
6810
6912
  AN ARTIFACT THE DEVICE ALREADY HOLDS IS NOT INSTALLED AGAIN
6811
6913
  Both platforms store the artifact verbatim, so its hash is its identity.
6812
- Before installing, Stim hashes the artifact it is about to install and the
6813
- one the device already has -- \`pm path\` then \`sha256sum\` on Android, the
6814
- \`simctl get_app_container\` bundle on iOS. Byte-identical means the install
6815
- is skipped. The phase still reports the cost of proving that identity, but
6816
- avoids the ~43s a 400MB APK can cost to copy and install over USB.
6914
+ Before installing, Stim compares the artifact it is about to install with
6915
+ the one the device already has. On iOS it hashes both bundles: the
6916
+ \`simctl get_app_container\` bundle and the one about to be installed.
6917
+ Byte-identical means the install is skipped. The phase still reports the
6918
+ cost of proving that identity, but avoids the ~43s a 400MB APK can cost to
6919
+ copy and install over USB.
6920
+
6921
+ On Android the proof is one \`adb shell\` call that returns the installed
6922
+ APK's path, size, and the SHA-256 of its tail: the v2 or v3 signing block,
6923
+ the central directory, and the end record. That tail carries the signed
6924
+ digests of every other byte, and Android verified them when it installed
6925
+ the APK. Stim verifies the local APK against the same digests once, with
6926
+ \`apksigner verify\`, and keeps the size and tail digest in
6927
+ \`apk-signed-tail.json\` beside the APK in its build cache entry (stored with
6928
+ the build; an older entry gets it on first use). The same size and tail
6929
+ digest mean the same file, on an owned emulator, an adopted one, a
6930
+ \`--device\` phone and a plain rerun. A different size or tail digest
6931
+ installs without hashing the whole APK. An APK outside a build cache entry,
6932
+ one without a v2 or v3 signing block, an apksigner that is missing or fails,
6933
+ and a device whose \`tail\` or \`stat\` does not answer as expected fall back
6934
+ to \`pm path\` then \`sha256sum\` of the whole installed APK.
6817
6935
 
6818
6936
  install unchanged (emulator-5584 already has this build) (0.4s)
6819
6937
 
@@ -6826,8 +6944,9 @@ AN ARTIFACT THE DEVICE ALREADY HOLDS IS NOT INSTALLED AGAIN
6826
6944
  install unchanged (stim-app already has this build) (0.4s)
6827
6945
  install dev client prepared (0.9s)
6828
6946
 
6829
- The skip needs PROOF. A package that is not installed, a split install, an
6830
- image without \`sha256sum\`, and any adb or simctl failure all read as
6947
+ The skip needs PROOF. A package that is not installed, a split install, a
6948
+ device that answers neither probe (no \`tail\` and no \`sha256sum\`), and any
6949
+ adb or simctl failure all read as
6831
6950
  "cannot determine", and the run installs exactly as it always did. A release
6832
6951
  run swaps this workspace's JS into a COPY of the artifact, which is a
6833
6952
  different artifact and is therefore always installed.
@@ -6838,9 +6957,10 @@ AN ARTIFACT THE DEVICE ALREADY HOLDS IS NOT INSTALLED AGAIN
6838
6957
  A RUNNING APP IS RESTARTED, LIKE XCODE'S RUN
6839
6958
  Before it launches, \`ios\` and \`android\` read the device's process list.
6840
6959
  When the app is already running, from an earlier run or after an install
6841
- that was skipped, Stim stops it and launches it fresh: \`simctl terminate\`
6842
- on a simulator, \`am force-stop\` on an emulator or an \`android --device\`
6843
- phone. The new process requests its bundle, so launch verification has real
6960
+ that was skipped, Stim stops it and launches it fresh: \`simctl launch
6961
+ --terminate-running-process\` on a simulator (a separate \`simctl
6962
+ terminate\` when the app opens through a URL), \`am force-stop\` on an
6963
+ emulator or an \`android --device\` phone. The new process requests its bundle, so launch verification has real
6844
6964
  evidence instead of an old process that fetched nothing. In-app state from
6845
6965
  the previous session does not survive. The launch line says so:
6846
6966
 
@@ -7027,6 +7147,67 @@ THE BUILD CACHE HAS THREE LEVELS
7027
7147
  trims, and clears local caches only: the provider contract has no delete
7028
7148
  operation, so no local command can remove data a team or CI system shares.
7029
7149
 
7150
+ WHAT THE KEY COUNTS. Stim runs @expo/fingerprint with its default
7151
+ \`balanced\` preset, the one the project's own Expo uses. An installed
7152
+ dependency, and a config plugin loaded from node_modules, counts by its name
7153
+ and version, not by its files. A package whose real path is outside
7154
+ node_modules -- a workspace package, a link: dependency, a local Expo module
7155
+ -- counts by its files, and so do patches/, the app's committed ios/ or
7156
+ android/, the evaluated app config and its local plugins. patches/ and
7157
+ .yarn/patches/ next to a monorepo's lockfile count too, and so do the patch
7158
+ hashes pnpm-lock.yaml records, wherever the patch files live. The app's
7159
+ version, ios.buildNumber and android.versionCode, eas.json, .easignore and
7160
+ the path overrides of react-native.config.js are left out, so a Release
7161
+ cache hit keeps the version it was built with. A project that wants native
7162
+ package files hashed sets \`preset: 'strict'\` in fingerprint.config.js, at
7163
+ about twice the fingerprint's CPU.
7164
+
7165
+ AN EDIT INSIDE node_modules/ IS NOT SEEN. Editing a dependency's native file
7166
+ in place (node_modules/<pkg>/ios, android, cpp, a podspec or build.gradle)
7167
+ leaves the key unchanged, so the next run installs the cached app without
7168
+ the edit. Make the change a patch (patch-package's patches/, or pnpm patch,
7169
+ whose patches the fingerprint hashes), or build it with
7170
+ \`--no-build-cache --remote-build local\`. --no-build-cache alone can still
7171
+ place the build on another machine, which installs dependencies from the
7172
+ lockfile and so builds without the edit. The fresh build is stored under the
7173
+ unchanged key in the machine-wide cache and handed to any cache provider, so
7174
+ every worktree reuses it, edit included, until the dependency's version or
7175
+ patches change; undo the edit with another --no-build-cache build.
7176
+
7177
+ A KNOWN TREE SKIPS THE FULL FINGERPRINT. After a full fingerprint at the
7178
+ first lookup of a React Native or Expo run, Stim records it in
7179
+ $STIM_HOME/fingerprint-memo (~/.stim by default) under a static digest of
7180
+ the tree: git blob ids of the native inputs git tracks, the contents of the
7181
+ untracked, modified and ignored ones, the app config's sources and the files
7182
+ and directories it names, each installed package's path, version and native
7183
+ markers, the lockfile, and the environment variables the config and its
7184
+ .env files read. Any worktree whose tree has the same digest, a fresh one
7185
+ included, reuses the record instead of evaluating the config and running
7186
+ autolinking:
7187
+
7188
+ fingerprint matches a recorded fingerprint of this tree; confirming it in the background
7189
+
7190
+ A full fingerprint then runs at low priority while the app installs and
7191
+ launches. The record is put back only when it agrees. If the run reaches its
7192
+ end first, it waits up to 3 seconds more, then gives the check up and leaves
7193
+ no record, so the next run fingerprints in full. A disagreement prints a
7194
+ warning naming both hashes at the end of the run and writes a
7195
+ fingerprint_memo_stale record to the build log; run the command again to use
7196
+ the current tree.
7197
+
7198
+ A config the digest cannot follow always fingerprints in full: a dynamic
7199
+ require or import, process.env used other than by literal name, file,
7200
+ process or network access while it is evaluated, a dotenv import, a
7201
+ fileHookTransform, or a preset that hashes package files. A local config
7202
+ plugin counts as evaluated too, with one exception: the digest does not
7203
+ track the files a plugin that applies mods reads, assuming it reads them in
7204
+ its mods at prebuild. A plugin that also puts what it reads into the config
7205
+ is a gap only the background check catches. So does a project outside git or on Yarn Plug'n'Play. The record is
7206
+ never consulted after prebuild or pod install, by a build worker, by doctor,
7207
+ or with --no-build-cache; ios --plan and android --plan read one without
7208
+ writing. Records are 0600, at most 256 per platform, and gc prunes them by
7209
+ age with the other caches.
7210
+
7030
7211
  A MISS EXPLAINS ITSELF. Each local cache entry keeps the per-source hashes
7031
7212
  of its fingerprint (hashes only, pruned with the entry). Before compiling,
7032
7213
  Stim compares the sources it is about to store with a baseline: the entry
@@ -7359,9 +7540,13 @@ OPT-IN CONCURRENCY LIMITS (UNLIMITED BY DEFAULT)
7359
7540
  status\` shows "shut down after 30m idle" on the device until the next
7360
7541
  \`ios\` or \`android\` run boots it again. When metro.idleStopMinutes is
7361
7542
  shorter, the dev server's idle stop first shuts down the devices idle that
7362
- long, because no supervisor is left to check afterwards. The supervisor
7363
- path does not check without a supervisor: release runs, and after
7364
- \`stim stop\`. The setting is read when the supervisor starts. Stim Desktop's simulator view is not a
7543
+ long, because no supervisor is left to check afterwards. Devices with no
7544
+ supervisor (release runs, a dev server that exited) are shut down by
7545
+ automatic maintenance while maintenance.mode is on (guide cleanup): every maintenance.idleCheckMinutes
7546
+ (5) a command starts a background check that applies each workspace's
7547
+ current setting with the same idle rules, native-run lock recheck, status
7548
+ and log record, and skips the workspace of the command that started it.
7549
+ The supervisor reads the setting when it starts. Stim Desktop's simulator view is not a
7365
7550
  stim-server client and does not count as a viewer. Change it with
7366
7551
  \`stim settings set devices.idleShutdownMinutes <minutes> --scope machine\`;
7367
7552
  0 turns it off, and an explicit 0 stays 0.
@@ -7575,11 +7760,18 @@ OPT-IN CONCURRENCY LIMITS (UNLIMITED BY DEFAULT)
7575
7760
  paths eligible under the source checkout's Git ignore rules, including .env
7576
7761
  and local configuration. The source's nonempty
7577
7762
  .worktreeexclude replaces its resolved worktree.exclude setting. Nested
7578
- registered worktrees, .DS_Store, .DerivedData, .idea, .jj, a project-local
7579
- .pnpm-store, and android/build/generated/autolinking caches are excluded,
7580
- including inside newly copied directories; pnpm refills its store from the
7581
- network or its global store. Gradle regenerates autolinking
7582
- for the destination checkout on its next build. Warm also skips paths
7763
+ registered worktrees and ignored entries named .DS_Store, .DerivedData,
7764
+ .idea, .jj or .pnpm-store are not copied, and neither is
7765
+ android/build/generated/autolinking. Warm never walks a copied directory, so
7766
+ a .DS_Store or .idea inside a copied directory stays. Warm deletes only
7767
+ these paths from the copy: .pnpm-store in a copied node_modules (pnpm
7768
+ refills it from the network or its global store), the autolinking directory
7769
+ under a copied android, android/build or android/build/generated (Gradle
7770
+ regenerates it for the destination checkout on its next build), and the
7771
+ expo-modules-jsi/apple/.DerivedData Xcode build state, directly under
7772
+ node_modules or in the pnpm virtual store. An ignored directory that Git
7773
+ reports above those names (for example an app directory with no tracked
7774
+ files) is copied as is. Warm also skips paths
7583
7775
  overlapping a nested destination worktree or below a symlink ancestor.
7584
7776
  Tracked .idea settings come from Git and stay untouched by warm.
7585
7777
 
@@ -8104,10 +8296,34 @@ THE POOL: WHICH DEVICE AN ID-LESS \`--device\` PICKS
8104
8296
 
8105
8297
  { "ios": { "simslimProfile": ".simslim/dev.json" } }
8106
8298
 
8299
+ To slim every Stim-owned iOS simulator on a Mac without a profile per app,
8300
+ set it on the machine layer:
8301
+
8302
+ stim settings set ios.simslim on --scope machine
8303
+
8304
+ With \`ios.simslim\` on and no \`ios.simslimProfile\`, Stim runs
8305
+ \`simslim on <udid>\` with SimSlim's default categories. A project profile
8306
+ always applies in place of the defaults. A workspace, repo or committed
8307
+ \`ios.simslim off\` opts that app out and restores stock services on its
8308
+ simulator. The defaults disable push, StoreKit, iCloud, Safari sync, Mail,
8309
+ Calendar and Contacts services, among others; move an app that needs them
8310
+ to a profile that keeps those categories, or to \`off\`. A running
8311
+ simulator is slimmed on the next \`stim ios\`, without a boot first, and a
8312
+ parked simulator when Stim adopts it. An already slim simulator is left
8313
+ alone, so only the first slim reboots it.
8314
+ Without the \`simslim\` command, doctor reports it and \`stim ios\` boots
8315
+ the simulator unslimmed with a one-line note. Hosted simulators on a named
8316
+ Mac are never slimmed, whatever either Mac's setting says.
8317
+
8107
8318
  SimSlim 0.8 requires iOS 18.5 or newer. On each local \`stim ios\`,
8108
8319
  Stim reconciles that profile on the owned simulator before the app build.
8109
- The first change can update services and reboot the simulator. A matching
8110
- profile is a fast no-op on later launches. The settings persist across normal
8320
+ The first change can update services and reboot the simulator. Stim records
8321
+ the applied profile (the default categories, or a hash of the profile file)
8322
+ and the boot of the simulator it was applied on, so a later launch on the
8323
+ same boot with the same profile skips the \`simslim\` call. A changed
8324
+ profile, a reboot, or a missing record reconciles again. A manual
8325
+ \`simslim off\` on a running simulator is not noticed until the simulator
8326
+ reboots or the profile changes. The settings persist across normal
8111
8327
  shutdowns and reboots. Removing the setting restores stock services when
8112
8328
  Stim applied the profile. Stim never changes an unowned or remote simulator.
8113
8329
  Each SimSlim operation has a 12-minute outer deadline, including discovery.
@@ -8204,13 +8420,14 @@ and logs, and never deletes anything. off disables it. It is off in CI and
8204
8420
  scoped STIM_HOME homes unless STIM_MAINTENANCE is explicit, so a scratch home
8205
8421
  or CI run never deletes on its own.
8206
8422
  Commands trigger a detached pass when a check is due: disk and memory pressure
8207
- (every minute), directory sizes (hourly), finished worktrees (every 15
8208
- minutes) and the age sweep (daily). guide, settings, help and every gc
8423
+ (every minute), idle devices (every 5 minutes, maintenance.idleCheckMinutes),
8424
+ directory sizes (hourly), finished worktrees (every 15 minutes) and the age
8425
+ sweep (daily). guide, settings, help and every gc
8209
8426
  invocation do not trigger it; gc without --delete prints the maintenance plan
8210
8427
  itself.
8211
8428
  status --watch and every running dev server supervisor also trigger checks
8212
8429
  each minute, so the disk check runs while no command does.
8213
- Size, worktree and sweep checks defer under high load. Attempts back off for at
8430
+ Size, idle, worktree and sweep checks defer under high load. Attempts back off for at
8214
8431
  least one minute. Measured directories are workspace build outputs and Stim's
8215
8432
  shared native, Metro, ccache, Swift compilation and registered caches. The size
8216
8433
  check also records the disk of each owned and parked simulator and emulator
@@ -8219,7 +8436,10 @@ Pressure checks read free disk on the Stim home, projects and worker root
8219
8436
  volumes. On macOS the memory signal is the sysctl pressure level, with no
8220
8437
  signal if sysctl fails. Other platforms use os.freemem(); macOS never falls
8221
8438
  back to it. Memory pressure is recorded only; stopping devices, dev servers
8222
- and helpers is not automatic. While the level is warning or critical, the
8439
+ and helpers for memory is not automatic. The idle check shuts down (never
8440
+ deletes) owned simulators and emulators idle for their workspace's
8441
+ devices.idleShutdownMinutes, also with no supervisor running, through the
8442
+ supervisor's idle rules and records (guide lifecycle budget). While the level is warning or critical, the
8223
8443
  check also names abnormally large processes (guide lifecycle simslim) in
8224
8444
  status's maintenance block and the maintenance log.
8225
8445
 
@@ -8236,13 +8456,25 @@ In on mode a pass runs, cheapest to rebuild first:
8236
8456
  claim (taken for each xcodebuild run that compiles with it) or that
8237
8457
  claim is unresolved; the emptying holds the exclusive claim
8238
8458
  5. linked worktrees whose branch or pull request finished (gc's finished
8239
- worktree rules and gc.worktreeGraceMinutes; maintenance.removeFinishedWorktrees)
8240
- 6. owned simulators and emulators of a finished worktree that is kept, such
8459
+ worktree rules and gc.worktreeGraceMinutes; maintenance.removeFinishedWorktrees),
8460
+ and, with maintenance.removeIdleWorktrees (off by default), in the age
8461
+ sweep clean, pushed ones unused for olderThanDays, as gc --delete
8462
+ --worktrees --older-than removes them, git-ignored files included; never
8463
+ a dirty one, one with unpushed commits, the source checkout, or one whose
8464
+ last use is unknown
8465
+ 6. parked simulators and emulators unused for olderThanDays in the age
8466
+ sweep, through centralized teardown with ownership checked again
8467
+ 7. owned simulators and emulators of a finished worktree that is kept, such
8241
8468
  as a dirty one (not one locked with git worktree lock), once it finished
8242
8469
  and was idle for gc.worktreeGraceMinutes (at least protectRecentHours),
8243
8470
  and of any
8244
8471
  workspace idle for maintenance.deviceIdleDays (3; 0 turns it off) in the
8245
8472
  age sweep. Devices are recreated on the next run.
8473
+ The age sweep runs every maintenance.sweepHours (24) in the background of
8474
+ commands, never at a set hour.
8475
+ maintenance.olderThanDays (7) is its age for steps 2, 3, 5 and 6; with 0 the
8476
+ sweep removes nothing in them by age, while step 1, step 7, finished
8477
+ worktrees and the cap and disk-driven parts of steps 2 and 3 still run.
8246
8478
  Disk-driven steps stop once free space is back above the floor. Kept for
8247
8479
  every pass: a workspace that is in use, holds a lock, was used within
8248
8480
  maintenance.protectRecentHours, is pinned with maintenance.keep, or is the
@@ -8338,6 +8570,23 @@ claim fails with ENOSPC or EDQUOT, the pass deletes it and retries once, so a
8338
8570
  pass starts at zero free disk as long as an earlier pass left the reserve; the
8339
8571
  pass writes it again at its end.
8340
8572
 
8573
+ WHAT WAS CLEARED
8574
+ Every run that deletes or changes something appends one record per item to
8575
+ the machine gc log, $STIM_HOME/gc/gc.ndjson: gc --delete with any flags, gc
8576
+ --idle, gc --cache --delete, each maintenance action taken, and what the budget gate
8577
+ reclaims before stim start, ios or android. A dry run writes nothing, and a
8578
+ gc run that only kept items writes nothing. Failed and kept items carry their
8579
+ reason. The file rotates at maintenance.logMaxMb and its old generation is
8580
+ kept for maintenance.logRetentionDays, like the maintenance log.
8581
+ stim logs --source gc the retained records, oldest first
8582
+ stim logs --source gc --json one record per line
8583
+ stim logs --source gc --errors only the items that failed
8584
+ gc works from any directory because the log is machine-wide; it cannot be
8585
+ combined with other --source values and --source all leaves it out. Fields
8586
+ (\`guide logs\`, GC LOG): runId, trigger (manual, desktop, maintenance or
8587
+ budget), options, kind, status (done, kept or failed), label, id, path,
8588
+ bytes, detail.
8589
+
8341
8590
  CLEANUP AND DISK
8342
8591
 
8343
8592
  worktree remove stops the agent-device daemon when the workspace's
@@ -8742,7 +8991,9 @@ SWEEPING FINISHED WORKTREES
8742
8991
  one, else by the RECENT ACTIVITY sources above being older than
8743
8992
  --older-than days (reason recently-used while they are newer, and
8744
8993
  last-use-unknown when they cannot be read). Automatic maintenance removes it
8745
- only at disk pressure level 3, once that activity is older than
8994
+ the same way in its daily age sweep, after maintenance.olderThanDays, only
8995
+ with maintenance.removeIdleWorktrees (off by default), and at disk pressure
8996
+ level 3 once that activity is older than
8746
8997
  maintenance.pressureProtectMinutes and at least 5 minutes old, whatever
8747
8998
  the setting; unreadable activity keeps it. Removal re-checks, under the removal
8748
8999
  locks, that HEAD did not move and the worktree is still idle, and
@@ -9417,6 +9668,13 @@ KEYS STIM READS
9417
9668
  with STIM_BAD_ARG a path that escapes the app or
9418
9669
  holds no .xcworkspace or .xcodeproj, and any value
9419
9670
  on an Expo app, whose prebuild writes ios/.
9671
+ ios.simslim "on" or "off"; default "off", machine or project
9672
+ layers. "on" slims every Stim-owned iOS simulator
9673
+ with SimSlim's default categories when no
9674
+ ios.simslimProfile is set; a profile always wins. A
9675
+ workspace, repo or committed "off" opts that app out
9676
+ of a machine "on" and restores stock services. See
9677
+ \`stim guide lifecycle simslim\`.
9420
9678
  ios.simslimProfile a SimSlim JSON profile under the app directory,
9421
9679
  at most 64 KiB. Install the
9422
9680
  external tool once with
@@ -9425,9 +9683,9 @@ KEYS STIM READS
9425
9683
  iOS work after reviewing service tradeoffs; see
9426
9684
  \`stim guide lifecycle simslim\`. Each local \`stim ios\`
9427
9685
  reconciles the profile on its Stim-owned simulator.
9428
- The first change can reboot it; a matching profile is a
9429
- fast no-op. Removing the setting restores stock services
9430
- when Stim applied the profile. Absolute paths, root or
9686
+ The first change can reboot it; an unchanged profile on
9687
+ the same boot is skipped. Removing the setting
9688
+ restores stock services when Stim applied the profile. Absolute paths, root or
9431
9689
  symlink escapes, and missing files are refused before
9432
9690
  simulator creation. Remote and unowned simulators are
9433
9691
  never changed.
@@ -9707,7 +9965,9 @@ ${ANDROID_AVD_CONFIG_HELP.map((line) => ` ${line}`).joi
9707
9965
  metro.warmupUrl.android
9708
9966
  an HTTP(S) URL or a /path ending in .bundle, with the
9709
9967
  full query the app uses, including a matching platform.
9710
- Unset uses Expo's manifest or bare React Native defaults.
9968
+ Unset uses the app's most recent bundle request from
9969
+ the workspace's metro log, then Expo's manifest or the
9970
+ bare React Native defaults.
9711
9971
  Stim preserves the path and query but always requests
9712
9972
  this workspace's verified local Metro port; a supplied
9713
9973
  host and port are ignored. No defaults are added to an
@@ -10016,6 +10276,11 @@ never decide who the worker is. With --fix, a pending entry is replaced when
10016
10276
  STIM_ACCESS_TICKET names another ticket.
10017
10277
  Approved doctor entries under remoteMachines and deviceHosts include host
10018
10278
  { name, screenRecording, accessibility } when the worker reports it.
10279
+ A remote machine doctor cannot use carries a diagnosis { layer, code, title,
10280
+ message, remedy } naming the first layer that failed: Tailscale on this machine,
10281
+ the peer, the serve route, stim-server, or compatibility. Tell the user its
10282
+ message and remedy; the remedies run on the remote machine or in Tailscale, so
10283
+ agents do not run them. \`stim guide facts doctor\` lists the codes.
10019
10284
 
10020
10285
  REMOTE MACHINES AND PHONE APP
10021
10286
  When the user mentions slow builds, a busy machine, a full disk or device limit, or
@@ -10161,10 +10426,14 @@ Stim connects to a named machine only while its name still belongs to the pinned
10161
10426
  node and never sends the token to another node. \`doctor\` reports each
10162
10427
  remote machine's build pairing state and, for an approved one, asks it for one build
10163
10428
  offer and lists every reason it would not take this app's iOS build: no
10164
- answer, another Stim build, CPU, Xcode, simulator SDK or CocoaPods, no
10429
+ answer, an incompatible Stim build, CPU, Xcode, simulator SDK or CocoaPods, no
10165
10430
  Bundler for an app whose Gemfile.lock pins CocoaPods, no iPhone simulator on
10166
10431
  the runtime \`stim ios\` builds for here, low disk, or busy. With Package.swift
10167
10432
  and no platform selection on a Mac, it also checks the macOS Xcode and SDK.
10433
+ Two machines build for each other when their Stim releases share a build
10434
+ compatibility version, which changes only when what a worker builds, how it
10435
+ builds, or the build messages change; their Stim builds can differ. A
10436
+ stim-server too old to report one needs the same Stim build as this machine.
10168
10437
 
10169
10438
  \`remote.build\` (machine scope, default auto) selects placement for
10170
10439
  \`ios\`, \`android\` and \`macos\`. \`--remote-build <auto|local|name>\`
@@ -10187,7 +10456,8 @@ at setup before consulting the cache; they create no build record or failed-run
10187
10456
  stats. A listed paired name with a cache hit compiles nothing, contacts no worker
10188
10457
  and records buildMachine with builtOn absent. Build failures retain the selected
10189
10458
  value; builtOn is here or the configured machine entry when compilation ran.
10190
- Prebuild and pod install still run on this machine before a named offload. A refusal
10459
+ Prebuild and pod install still run on this machine before a named offload; stim ios
10460
+ checks the machine's toolchain first. A refusal
10191
10461
  starts no local xcodebuild, Gradle or SwiftPM compile.
10192
10462
  A React Native offload sends the tracked and untracked files, and the gitignored
10193
10463
  files the native fingerprint hashes or the Expo config reads, such as .env.local
@@ -10284,18 +10554,44 @@ match. Completed archives survive worker checkout changes or removal; hits
10284
10554
  need no source upload or build slot. Disabled remote artifact reads skip this
10285
10555
  lookup. Release and physical-device builds keep their existing paths.
10286
10556
  SwiftPM, compiler and dependency caches use their existing paths.
10287
- For iOS the machine's Xcode and simulator SDK must match, and it needs an
10288
- iPhone simulator on the target runtime. Its project-selected CocoaPods must match too, unless
10289
- the app's Gemfile.lock, in ios/ or the project root, pins CocoaPods: both machines then run that version through
10290
- bundler, so the machine needs only Bundler on its stim-server PATH and
10557
+ For iOS the machine's Xcode and simulator SDK must match. Its offer reports the
10558
+ developerDir xcode-select prints and the Xcode apps in /Applications (version,
10559
+ build, and license and first-launch checks while no Xcode works), so doctor's
10560
+ fix for no usable Xcode is the exact command to run there: sudo xcode-select -s
10561
+ <the Xcode with this machine's build, else the newest>/Contents/Developer when
10562
+ the Command Line Tools or another Xcode is selected, sudo xcodebuild -license
10563
+ accept and sudo xcodebuild -runFirstLaunch for the checks that fail, or xcodes
10564
+ install <version> with no Xcode app. A runtime the machine lacks gets
10565
+ xcodebuild -downloadPlatform iOS -buildVersion <version>, or a pointer to the
10566
+ Xcode problem when xcode-select is the cause. A stim-server that reports no
10567
+ developerDir keeps the prose fix. It also needs an
10568
+ iPhone simulator on the target runtime. A missing or different global CocoaPods
10569
+ keeps automatic placement from choosing the machine for a project that does not
10570
+ pin it, unless the machine already holds the exact artifact; the placement line
10571
+ says why and doctor shows the install command. A named --remote-build still
10572
+ sends the build, and a pod install that fails there reports "pod install failed"
10573
+ with the fix on that machine. When the app's Gemfile.lock, in ios/ or the project
10574
+ root, pins CocoaPods, both machines run that version through
10575
+ bundler, so the machine needs Bundler on its stim-server PATH and
10291
10576
  installs the pinned gems itself on the first build. This Mac's side of the
10292
10577
  comparison runs \`pod --version\` in the directory pod install runs in. The comparison selects
10293
10578
  the app's .ruby-version when installed, otherwise rvm's or rbenv's default Ruby, and only when neither exists the GEM_HOME, GEM_PATH and Ruby-related PATH entries of the machine's login shell (read once with $SHELL -lic), unless the caller already sets GEM_HOME, with pod install's UTF-8 locale defaults. pod install, including the Bundler run, uses the same environment.
10294
10579
  For Android its CPU may differ, since the APK's ABIs come from the NDK, so an
10295
10580
  x86_64 Linux or Windows machine running stim-server can build for an Apple silicon Mac. Its JDK major
10296
- version must match, and its Android SDK must hold the NDK, build-tools and
10581
+ version must match this machine's, which can be an installed JDK that is not the
10582
+ worker's default: its offer lists \`jdks\`, and a build sent with the build-jdk
10583
+ feature runs with JAVA_HOME at the one of that major. A major no installed JDK
10584
+ has gets the install command for the worker's OS as doctor's fix. A nearby major
10585
+ never counts (Gradle, AGP and Kotlin support and the shared build cache assume
10586
+ one JDK). Also, its Android SDK must hold the NDK, build-tools and
10297
10587
  compile platform that the project's React Native version names in
10298
- gradle/libs.versions.toml; Gradle and AGP come from the synced project. When
10588
+ gradle/libs.versions.toml; Gradle and AGP come from the synced project. A
10589
+ package the SDK lacks is a note, not a blocker, when its offer reports the SDK
10590
+ licenses accepted (\`licenses/android-sdk-license\`) and the app's
10591
+ Gradle root's gradle.properties does not set android.builder.sdkDownload=false:
10592
+ Gradle downloads the package on the first build there (the NDK is about 1-2 GB).
10593
+ Otherwise it blocks, and doctor's fix is the sdkmanager commands to run on that
10594
+ machine; a stim-server that does not report its licenses keeps that rule. When
10299
10595
  no machine takes the build in auto placement, the run prints one line with each machine's
10300
10596
  reason, such as \`janics-mac-mini: busy (load at or above 2/core; load
10301
10597
  8.2/core, 2 builds) -> building here\`, and compiles here. It sends the files
@@ -11053,8 +11349,8 @@ to the repository root; the worker resolves them inside its checkout.
11053
11349
 
11054
11350
  With --remote <machine> and --remote-build naming the same machine (the same
11055
11351
  paired node), the app is built and run there, so this Mac's Xcode, macOS SDK and
11056
- CPU architecture do not have to match: the worker needs the same Stim build and
11057
- its own Xcode and macOS SDK. This Mac's cache is skipped and the worker's cache
11352
+ CPU architecture do not have to match: the worker needs the same Stim build
11353
+ compatibility version and its own Xcode and macOS SDK. This Mac's cache is skipped and the worker's cache
11058
11354
  identity comes from the worker's toolchain. Any other combination keeps the match.
11059
11355
 
11060
11356
  The client validates the development plist and resource entries before asking a machine. It verifies