stim 1.20.1 → 1.21.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 (217) hide show
  1. package/README.md +94 -11
  2. package/dist/acquire-artifact-CD4lbiWE.mjs +153 -0
  3. package/dist/act-DHAw2A2S.mjs +2667 -0
  4. package/dist/activity-BzVfHSrY.mjs +1376 -0
  5. package/dist/{agent-device-usage-output-VPaOgGaF.mjs → agent-device-usage-output-zh1vhbLp.mjs} +21 -66
  6. package/dist/{android-CZh6ElG5.mjs → android-BEJ8Jpl7.mjs} +3 -3
  7. package/dist/{android-CLbBJ2ve.mjs → android-BLgbNqox.mjs} +128 -79
  8. package/dist/{android-eXfIjKJr.mjs → android-CV_GCELZ.mjs} +5 -5
  9. package/dist/{android-DE8-JfGW.mjs → android-DUYkNF4u.mjs} +188 -106
  10. package/dist/{android-cas-BFpS84v2.mjs → android-cas-Ct5b1xCK.mjs} +7 -6
  11. package/dist/android-cas-compiler.mjs +2 -2
  12. package/dist/android-project-BCAAmMwz.mjs +12 -0
  13. package/dist/api.d.mts +740 -8
  14. package/dist/api.mjs +585 -1
  15. package/dist/{artifact-Ctb2jpw6.mjs → artifact-B8T3S5gi.mjs} +223 -201
  16. package/dist/{artifact-DXk_Fle_.mjs → artifact-DAuZ-FNU.mjs} +158 -148
  17. package/dist/{browser-web-xQWC2RWl.mjs → browser-web-QQzVjz3O.mjs} +28 -26
  18. package/dist/budget-Bs6SxxzO.mjs +9154 -0
  19. package/dist/{build-COBA1tBc.mjs → build-CoPzQRNw.mjs} +144 -101
  20. package/dist/build-cache-DmyR9Zzc.mjs +602 -0
  21. package/dist/build-plan-CaFskB7_.mjs +188 -0
  22. package/dist/{cache-manifest-qA3dT2x2.mjs → cache-manifest-DkkqguoK.mjs} +10 -5
  23. package/dist/cache-manifest.mjs +1 -1
  24. package/dist/{ccache-CtbqWxIC.mjs → ccache-BJRZ9pG3.mjs} +10 -9
  25. package/dist/{cdp-Dq_ypAlJ.mjs → cdp-CWmIUMbw.mjs} +18 -5
  26. package/dist/{chrome-Dp2lMLBe.mjs → chrome-B6tfCta1.mjs} +7 -4
  27. package/dist/{cli-sGqlPu1n.mjs → cli-0DYf8QhQ.mjs} +20 -20
  28. package/dist/cli.mjs +1 -1
  29. package/dist/client-B7QVOlCr.d.mts +253 -0
  30. package/dist/client-UXD70ZbM.mjs +1406 -0
  31. package/dist/collector-run.d.mts +4 -39
  32. package/dist/collector-run.mjs +9 -9
  33. package/dist/{command-output-CPDq_90S.mjs → command-output-B1UrjZHL.mjs} +1 -1
  34. package/dist/config-CYcBF478.mjs +949 -0
  35. package/dist/{created-devices-CXBMtoj5.mjs → created-devices-CqeBlrEn.mjs} +3 -3
  36. package/dist/{dependency-state-B2m8eqsS.mjs → dependency-state-BbKQW83h.mjs} +20 -5
  37. package/dist/{detached-entry-BXq4fIl-.mjs → detached-entry-DFRyG1_o.mjs} +1 -1
  38. package/dist/{dev-client-BqYjMIaC.mjs → dev-client-UOeESKm6.mjs} +2 -2
  39. package/dist/device-DCxH7_By.mjs +1068 -0
  40. package/dist/{device-D4DqjSZZ.mjs → device-ExJq8Ei7.mjs} +35 -36
  41. package/dist/{device-capacity-DrwHwR97.mjs → device-capacity-CMlqEcvw.mjs} +43 -14
  42. package/dist/device-host-worker.mjs +65 -62
  43. package/dist/{device-ios-DE8KJd3b.mjs → device-ios-DRUeKaFV.mjs} +49 -49
  44. package/dist/{device-lease-run-Baelt3nc.mjs → device-lease-run-C8raGFoP.mjs} +14 -26
  45. package/dist/{device-pool-DtK9S6AD.mjs → device-pool-DMRpVX9a.mjs} +5 -5
  46. package/dist/doctor-BLHCYxzi.mjs +2 -0
  47. package/dist/{doctor-Dunu3Nv1.mjs → doctor-DTvaDOda.mjs} +317 -344
  48. package/dist/{doctor-DiP9HsIP.mjs → doctor-Dhqebm0q.mjs} +183 -91
  49. package/dist/doctor-fingerprint.mjs +19 -0
  50. package/dist/{doctor-watchman-XJzT8tK-.mjs → doctor-watchman-Cv1F10Kj.mjs} +40 -23
  51. package/dist/{eas-build-0gadvrca.mjs → eas-build-BomfRnFT.mjs} +53 -20
  52. package/dist/{build-plan-CRjAA6Qi.mjs → eas-fallback-DJwZpy0c.mjs} +26 -208
  53. package/dist/{watchman-CqmaGcIb.mjs → eas-session-ledger-op6A54yi.mjs} +229 -11
  54. package/dist/{error-diagnostics-BhQtoLrZ.mjs → error-diagnostics-Bf0rZvG9.mjs} +172 -82
  55. package/dist/exec-H20xSg2T.d.mts +65 -0
  56. package/dist/exec-_UbtCQcG.mjs +750 -0
  57. package/dist/gc-C-WwL0yi.mjs +3 -0
  58. package/dist/{gc-D5cz5G0k.mjs → gc-_-dCXdyL.mjs} +625 -786
  59. package/dist/git-C3iTsJaZ.mjs +73 -0
  60. package/dist/{guide-DakjcoUt.mjs → guide-Yqwt2bd6.mjs} +990 -280
  61. package/dist/{hosted-android-C-D8Dj0_.mjs → hosted-android-Bw-honsd.mjs} +8 -6
  62. package/dist/{hosted-client--hNFgixu.mjs → hosted-client-DB_gBPfY.mjs} +47 -24
  63. package/dist/{hosted-ios-Chx9XQyI.mjs → hosted-ios-CUvOf4Vr.mjs} +1 -1
  64. package/dist/{hosted-logs-Bkz0Hxzh.mjs → hosted-logs-DL_P5AdN.mjs} +77 -43
  65. package/dist/{hosted-macos-ckTjN-0V.mjs → hosted-macos-QI141yKk.mjs} +21 -15
  66. package/dist/{hosted-native-3KXZ5-qn.mjs → hosted-native-Ksyjgzq-.mjs} +31 -20
  67. package/dist/idle-BVeRGroT.d.mts +24 -0
  68. package/dist/{idle-shutdown-DhAUyf5s.mjs → idle-shutdown-DaKmZi0z.mjs} +11 -10
  69. package/dist/{ios-BDBN-xpf.mjs → ios-CQ68hG9s.mjs} +8 -8
  70. package/dist/{ios-o896T9PE.mjs → ios-CpByZy12.mjs} +141 -81
  71. package/dist/{ios-device-B1B14xND.mjs → ios-device-CXGP1bsD.mjs} +1 -1
  72. package/dist/{ios-device-BWHKGaJq.mjs → ios-device-CwFMehUS.mjs} +60 -35
  73. package/dist/{ios-project-BeOXKPt2.mjs → ios-project-WuwwBdvW.mjs} +13 -13
  74. package/dist/{ios-VGvNdsv2.mjs → ios-qNJt4dFm.mjs} +1 -1
  75. package/dist/ios-signing-DTTIChGs.mjs +489 -0
  76. package/dist/{ios-state-BsXMwo5y.mjs → ios-state-DvFX-K_l.mjs} +1 -1
  77. package/dist/{ios-CQ8bHQXB.mjs → ios-ubFsBzwq.mjs} +129 -86
  78. package/dist/{js-swap-Z_mSuRrx.mjs → js-swap-Cc1dttps.mjs} +60 -33
  79. package/dist/{launch-V1zqiOYn.mjs → launch-CD1yVc7U.mjs} +92 -91
  80. package/dist/{launch-BootbMk4.mjs → launch-Zq2mOSDy.mjs} +87 -545
  81. package/dist/logs-BS00Anxf.mjs +352 -0
  82. package/dist/logs-CMm05HxN.mjs +255 -0
  83. package/dist/macos-B65--pPd.mjs +2 -0
  84. package/dist/{macos-D7irBW3A.mjs → macos-C-0qJ6zq.mjs} +244 -215
  85. package/dist/macos-run.mjs +1 -1
  86. package/dist/maintenance-run.mjs +122 -247
  87. package/dist/{metro-_ZIE3Axg.mjs → metro-DEXpIhHC.mjs} +71 -17
  88. package/dist/{metro-gateway-BzlQZQmU.mjs → metro-gateway-BMJNlj3E.mjs} +20 -12
  89. package/dist/{named-ports-O1TIl7tx.mjs → named-ports-BWTzt6mu.mjs} +8 -64
  90. package/dist/native-android-JACyjTZs.mjs +844 -0
  91. package/dist/{native-gradle-inputs-BQnIdAlL.mjs → native-gradle-inputs-Bep9JcSF.mjs} +128 -147
  92. package/dist/native-inputs-TRTTTRxt.mjs +91 -0
  93. package/dist/native-run-DY_RLx3C.mjs +277 -0
  94. package/dist/{native-runtime-D024BAvm.mjs → native-runtime-Dg1Q7tfF.mjs} +10 -10
  95. package/dist/{native-xcode-inputs-UElfGs59.mjs → native-xcode-inputs-CB2iQeWq.mjs} +28 -20
  96. package/dist/{native-xcode-ios-CjS-nDVY.mjs → native-xcode-ios-CXX39F7o.mjs} +96 -47
  97. package/dist/{ndjson-GOnbyvso.mjs → ndjson-CgE_o3Dl.mjs} +3 -3
  98. package/dist/offload-worker.d.mts +1 -46
  99. package/dist/offload-worker.mjs +213 -86
  100. package/dist/{ownership-BrjepDiv.mjs → ownership-DEmM5vhh.mjs} +3 -3
  101. package/dist/{page-Bv9s2KwY.mjs → page-CPaHeYqj.mjs} +7 -4
  102. package/dist/paths-BsJMoPP2.mjs +12 -0
  103. package/dist/paths-DQtTgsBf.mjs +42 -0
  104. package/dist/{placement-log-BItXET1x.mjs → placement-log-BqGbTa61.mjs} +5 -5
  105. package/dist/{plan-placement-B466UYMU.mjs → plan-placement-Bwj-IjY1.mjs} +6 -6
  106. package/dist/{ports-BFRL6JTj.mjs → ports-D-MmCTdv.mjs} +3 -3
  107. package/dist/prebuild-uD_ipCxF.mjs +449 -0
  108. package/dist/{project-DX8fXyw_.mjs → project-BZNjmW_8.mjs} +65 -38
  109. package/dist/{pull-request-vOhLNmIb.mjs → pull-request-Lfetn8Kr.mjs} +5 -18
  110. package/dist/pull-request-zjCSAryH.d.mts +120 -0
  111. package/dist/pull-requests.d.mts +1 -119
  112. package/dist/pull-requests.mjs +1 -1
  113. package/dist/{react-native-android-CuXM3QS8.mjs → react-native-android-DuzH0cn3.mjs} +83 -53
  114. package/dist/{gradle-CZZo1TSA.mjs → react-native-build-rhk4smwQ.mjs} +292 -15
  115. package/dist/react-native-doctor-CQpuIMso.mjs +234 -0
  116. package/dist/{react-native-ios-DtVO4qf2.mjs → react-native-ios-Bhq1rJeV.mjs} +46 -28
  117. package/dist/{recordings-Bk-VivUV.mjs → recordings-DYteQSu4.mjs} +12 -9
  118. package/dist/{reload-D9V7D8gP.mjs → reload-CmTRFJaX.mjs} +95 -49
  119. package/dist/reload-DzjSl5P_.mjs +2 -0
  120. package/dist/{remote-CZiTijxG.mjs → remote-ByYkXdGD.mjs} +15 -10
  121. package/dist/result-DNiS-tTP.mjs +434 -0
  122. package/dist/{run-ClC8UVB5.mjs → run-D23HmJMs.mjs} +4 -5
  123. package/dist/selection-CSl9i5qU.mjs +619 -0
  124. package/dist/{server-bare-dWjAarki.mjs → server-bare-CxTrvDg8.mjs} +3 -4
  125. package/dist/{server-command-DnJYLnr4.mjs → server-command-CtzloH3v.mjs} +14 -7
  126. package/dist/server-expo-C-90EhcG.mjs +2 -0
  127. package/dist/{server-expo-CUuzP9Mp.mjs → server-expo-DT_F_uVI.mjs} +51 -15
  128. package/dist/{settings-Bbztm4oV.mjs → settings-CsJDEXgK.mjs} +301 -132
  129. package/dist/{settings-vk3mJGSm.d.mts → settings-DkdmlelE.d.mts} +6 -4
  130. package/dist/settings-T1gtc3bf.mjs +114 -0
  131. package/dist/settings-write-HyVZ8eQ8.mjs +359 -0
  132. package/dist/settings.schema.json +163 -16
  133. package/dist/{simslim-ChMfe8rg.mjs → simslim-Cgk17SXV.mjs} +14 -13
  134. package/dist/{slot-launch-CeCRr6MK.mjs → slot-launch-DF8VfT64.mjs} +15 -20
  135. package/dist/{spawn-entry-Dcb7sDUz.mjs → spawn-entry-jw19m8Nl.mjs} +0 -1
  136. package/dist/stage-BIS0ydsV.mjs +183 -0
  137. package/dist/{start-CRkG-mZ7.mjs → start-7lpH7hmN.mjs} +66 -48
  138. package/dist/{start-mh6bS27z.mjs → start-RChynRDp.mjs} +1 -1
  139. package/dist/{state-lqxdOH_g.mjs → state-C4ZtUisW.mjs} +41 -14
  140. package/dist/{state-BUpAmt2r.mjs → state-XMziexv2.mjs} +3 -2
  141. package/dist/{stats-CaB1MxMi.mjs → stats-53T7OTb9.mjs} +7 -13
  142. package/dist/stats-report-BFfMpWWF.mjs +16 -0
  143. package/dist/{status-C7vzkInu.mjs → status-BL7jVyAB.mjs} +538 -467
  144. package/dist/status-CSi6tlRq.mjs +2 -0
  145. package/dist/{status-BDGHwkCs.mjs → status-DqEUSSxs.mjs} +6 -4
  146. package/dist/{status-watch-ZaVqRdtW.mjs → status-watch-GDCfqpTE.mjs} +187 -22
  147. package/dist/status-worker.d.mts +1 -0
  148. package/dist/status-worker.mjs +19 -0
  149. package/dist/stim-desktop-C12we7lz.mjs +112 -0
  150. package/dist/{stop-BU7JaFCg.mjs → stop-Bk_1qiV6.mjs} +2 -2
  151. package/dist/{stop-BO80KJoT.mjs → stop-CPzlM1L-.mjs} +138 -99
  152. package/dist/{stop-Cftw_sXJ.mjs → stop-D7h6jAXQ.mjs} +11 -10
  153. package/dist/supervisor-run.d.mts +8 -17
  154. package/dist/supervisor-run.mjs +63 -28
  155. package/dist/{support-Ca6PZbpr.mjs → support-67vraa1Q.mjs} +18 -11
  156. package/dist/{support-Co0wk8Cv.mjs → support-Dlu37g6H.mjs} +12 -12
  157. package/dist/{swiftpm-macos-siFSkmCO.mjs → swiftpm-macos-p8WZbxKU.mjs} +35 -24
  158. package/dist/toolchain-DhKwp9iI.mjs +2 -0
  159. package/dist/toolchain-Hp9sCpK4.mjs +873 -0
  160. package/dist/{trigger-C0MWBi8O.mjs → trigger-BAahxUPq.mjs} +20 -5
  161. package/dist/trigger-Bf3uDcpv.mjs +2 -0
  162. package/dist/{warm-progress-5FAXkXIg.mjs → warm-progress-BHX5r0Ei.mjs} +252 -176
  163. package/dist/{web-BTjzE8mc.mjs → web-CJmIj6AI.mjs} +18 -13
  164. package/dist/web-DknQETPs.mjs +2 -0
  165. package/dist/web-run.mjs +16 -17
  166. package/dist/windows-report.d.mts +1 -0
  167. package/dist/windows-report.mjs +18 -0
  168. package/dist/windows-report.ps1 +43 -0
  169. package/dist/workspace-process-lock-1_1OoVW5.mjs +57 -0
  170. package/dist/{workspace-state-CvgTFe-h.mjs → workspace-state-Cjuigeeu.mjs} +70 -56
  171. package/dist/{workspaces-C4kqIOlx.mjs → workspaces-BccMFmGQ.mjs} +1188 -1080
  172. package/dist/worktree-CK2MpJW5.mjs +2 -0
  173. package/dist/{worktree-DtRsh6go.mjs → worktree-DQ6Zi2Mv.mjs} +368 -110
  174. package/dist/xcode-Zlx3qjEs.mjs +896 -0
  175. package/package.json +7 -5
  176. package/shim/android-optimizations.gradle +7 -5
  177. package/dist/activity-BaMpUQ4-.mjs +0 -629
  178. package/dist/api-run.mjs +0 -296
  179. package/dist/api-zuX3ynYN.mjs +0 -154
  180. package/dist/app-install-Cg9YJy3x.mjs +0 -873
  181. package/dist/artifact-lifecycle-DzPpBYvK.mjs +0 -30
  182. package/dist/budget-RFbwFpG5.mjs +0 -4109
  183. package/dist/build-facts-DcLv2HSd.d.mts +0 -111
  184. package/dist/client-zqjFd79D.mjs +0 -1779
  185. package/dist/config-D0LhTY3S.mjs +0 -524
  186. package/dist/device-BgNj-qVj.mjs +0 -290
  187. package/dist/device-lease-C309csaB.mjs +0 -1228
  188. package/dist/device-remote-DjsNw8DD.mjs +0 -1272
  189. package/dist/eas-session-ledger-DJDeL4Uq.mjs +0 -205
  190. package/dist/exec-B81Qodjn.mjs +0 -254
  191. package/dist/launch-verify-Dl78EVgR.mjs +0 -428
  192. package/dist/logs-Bh4eqCCc.mjs +0 -384
  193. package/dist/macos-kivF8qKz.mjs +0 -2
  194. package/dist/miss-reason-SuTptY-I.mjs +0 -238
  195. package/dist/native-android-D94RlJ-X.mjs +0 -226
  196. package/dist/ndjson-Du3j0AQ9.d.mts +0 -17
  197. package/dist/plan-Jwu_vr-F.mjs +0 -260
  198. package/dist/prebuild-DB_zLOLc.mjs +0 -213
  199. package/dist/preview-BRLFRhTz.mjs +0 -1004
  200. package/dist/process-identity-Biv3arCO.mjs +0 -40
  201. package/dist/react-native-build-JUF31S8U.mjs +0 -239
  202. package/dist/react-native-doctor-BQHqiXaQ.mjs +0 -78
  203. package/dist/remote-cache-fxcl5C-L.mjs +0 -526
  204. package/dist/result-BrfgXtdO.mjs +0 -186
  205. package/dist/server-expo-Dvkd7JcF.mjs +0 -2
  206. package/dist/settings-BuKMOANA.mjs +0 -402
  207. package/dist/spawn-claims-Kqiifsk2.mjs +0 -72
  208. package/dist/stage-BNKKdDNc.mjs +0 -157
  209. package/dist/stim-desktop-8GQX2OV8.mjs +0 -63
  210. package/dist/tailnet-D7-tE_ug.mjs +0 -265
  211. package/dist/toolchain-B12psPA2.mjs +0 -2
  212. package/dist/toolchain-SqehyPLw.mjs +0 -1657
  213. package/dist/trigger-SHLOWJ4O.mjs +0 -2
  214. package/dist/web-CHN5gh34.mjs +0 -2
  215. package/dist/worktree-Cgnm6vZt.mjs +0 -2
  216. package/dist/{api-run.d.mts → doctor-fingerprint.d.mts} +0 -0
  217. package/dist/{errors-DzkMYh8Q.mjs → errors-ccaPc4ox.mjs} +2 -2
@@ -1,8 +1,8 @@
1
- import { t as ANDROID_AVD_CONFIG_HELP } from "./settings-Bbztm4oV.mjs";
2
- import { r as findProjectRoot } from "./project-DX8fXyw_.mjs";
3
- import { s as RECENT_LAUNCH_MS } from "./watchman-CqmaGcIb.mjs";
4
- import { o as guideStatus, t as WATCHMAN_NESTED_WORKTREES } from "./doctor-watchman-XJzT8tK-.mjs";
5
- import "./status-watch-ZaVqRdtW.mjs";
1
+ import { t as ANDROID_AVD_CONFIG_HELP } from "./settings-CsJDEXgK.mjs";
2
+ import { i as findProjectRoot$1 } from "./project-BZNjmW_8.mjs";
3
+ import { h as RECENT_LAUNCH_MS } from "./eas-session-ledger-op6A54yi.mjs";
4
+ import { o as guideStatus, t as WATCHMAN_NESTED_WORKTREES } from "./doctor-watchman-Cv1F10Kj.mjs";
5
+ import "./status-watch-GDCfqpTE.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
@@ -117,7 +117,7 @@ other apps or other workspaces' devices.
117
117
  stim ios # or: stim android, or: stim web
118
118
 
119
119
  When Stim Desktop is installed, worktree warm, start, ios, android and web
120
- print "Open in Stim Desktop: <link>" on stderr (links.desktop in JSON). The
120
+ print "Open in Stim: <link>" on stderr (links.desktop in JSON). The
121
121
  first time one prints it in a workspace, tell the user once: "You can watch
122
122
  this workspace in Stim Desktop: <link>". Do not repeat it after later
123
123
  commands. When no command prints it, do not mention Stim Desktop.
@@ -146,9 +146,9 @@ lifecycle builds.
146
146
  Agents never set STIM_ACCESS_TICKET, run stim-server setup, edit remote.*,
147
147
  server.* or hosting.* for the user, or approve requests. Read stim guide settings for
148
148
  Desktop access tickets and person-owned approval.
149
- Read stim guide settings to select a remote Mac. Keep a requested named
149
+ Read stim guide settings to select a remote machine. Keep a requested named
150
150
  placement when recovering STIM_OFFLOAD_REFUSED: report its reason and remedy.
151
- When the user mentions slow builds, a busy Mac or wanting updates away from the desk, tell them about Stim Desktop > Settings > Remote Macs > Add or Phones > Pair a Phone; see stim guide settings.
151
+ When the user mentions slow builds, a busy machine or wanting updates away from the desk, tell them about Stim Desktop > Settings > Remote Machines > Add or Phones > Pair a Phone; see stim guide settings.
152
152
 
153
153
  Read stim guide lifecycle concurrency when a build waits on another workspace
154
154
  or a build call times out. A native build can outlive a shell timeout; if the
@@ -260,6 +260,12 @@ holds it across runs; stim device unlock gives it back. Never delete another
260
260
  workspace's lease file under ~/.stim/device-locks; gc --delete removes expired
261
261
  ones.
262
262
 
263
+ stim device provision ios [udid] registers a phone with the project's Apple
264
+ Developer team and creates or refreshes its Development profile. It changes
265
+ the team's developer account, so never run it, and never pass it --yes,
266
+ without the user's explicit go-ahead for that step. When ios --device fails
267
+ on signing, report the remedy and ask the user.
268
+
263
269
  stop and worktree remove release this workspace's leases. On a physical
264
270
  iPhone, stop also closes the app by ending its log collector; it does not
265
271
  shut down the phone or uninstall the app.
@@ -412,7 +418,7 @@ FULL TOPIC LIST
412
418
  //#endregion
413
419
  //#region src/guide/api.ts
414
420
  var api_default = {
415
- summary: "Typed lifecycle API: createStim, build, run, stop, diagnostics, and cancellation",
421
+ summary: "Typed lifecycle API: createStim, build, run, reload, stop, diagnostics, doctor, GC reports, settings, stats, logs, and cancellation",
416
422
  body: () => `PROGRAMMATIC API
417
423
 
418
424
  Install stim as a project dependency: npm install --save-dev stim.
@@ -430,11 +436,78 @@ Import { createStim, StimError } from 'stim'. Node 22.12 or later is required.
430
436
  }
431
437
  }
432
438
 
433
- createStim takes projectRoot, optional absolute home and buildCache paths,
434
- and onProgress({ stream, message }). message is an output chunk. Without
435
- onProgress, operations write nothing to the caller's stdout or stderr. Each
436
- operation uses a bundled worker that calls the same lifecycle operations as
437
- the CLI; it never parses CLI arguments or changes the caller's cwd/environment.
439
+ createStim takes projectRoot, optional absolute home and buildCache paths, an
440
+ env object (default process.env) that Stim reads its settings overrides from
441
+ and passes to its child processes, and onProgress({ stream, message }). message is an
442
+ output chunk. Without onProgress, operations write nothing to the caller's
443
+ stdout or stderr. The home is fixed when the client is created: home, else
444
+ STIM_HOME from env, else the default home. All operations run in the calling
445
+ process against the client's home and project and call the same lifecycle
446
+ operations as the CLI. None parses CLI arguments or changes the caller's
447
+ cwd/environment.
448
+
449
+ settings() returns the stim settings --json payload: every setting with its
450
+ effective value, origin and per-layer values. An invalid environment override
451
+ rejects with StimError STIM_BAD_ARG and its remedy. stats() returns the
452
+ stim stats --json payload with the last recorded agent-device and SwiftPM cache
453
+ usage instead of measuring them again.
454
+
455
+ setSetting(key, text, { scope?, signal?, beforeWrite? }) and
456
+ unsetSetting(key, { scope?, signal?, beforeWrite? }) share CLI validation and
457
+ return its masked JSON write result. beforeWrite(workspace) runs once after
458
+ resolving the target and before mutation or recording-lock waits; throwing
459
+ refuses the write. Server adapters use it to admit that exact workspace.
460
+ Later registry changes do not redirect the write or its returned setting.
461
+ Pending writes cancel. After commit, recording deletion finishes before the
462
+ promise settles, including when cancellation arrives during cleanup.
463
+
464
+ doctor({ platform? }) and gc({ cache?, olderThan?, worktrees? }) read reports in
465
+ process with a 60 second execution budget. They accept signal and attempt
466
+ owned cleanup before rejecting. Deadline expiry rejects with
467
+ STIM_REPORT_TIMEOUT; a caller abort rejects with STIM_CANCELLED. A thrown
468
+ onProgress callback cancels the report and is propagated unchanged.
469
+ If child identity cannot be verified after a one-second retry budget, cleanup
470
+ refuses with STIM_CLAIM_UNAVAILABLE instead of cancellation or timeout. The
471
+ child cleanup error names the launcher PID; unverifiable processes and descendants are left
472
+ untouched. On POSIX, a launcher that exited before identity capture can only finish
473
+ successfully once its process group is proven gone.
474
+ After a termination request, cleanup waits at most one second for a child to
475
+ close. Overall cleanup has a 35 second budget, including temporary-worktree
476
+ removal; unfinished cleanup returns STIM_CLAIM_UNAVAILABLE and leaves unresolved
477
+ resources in place. Credential refresh waits asynchronously and checks
478
+ cancellation before writing.
479
+ On Windows, report probes require the built-in Windows PowerShell for process-tree containment.
480
+
481
+ doctor returns the doctor --json findings and metadata, with runningPath null
482
+ because the caller imports the library rather than executing a Stim CLI. It
483
+ never repairs the project. By default it does not record a check; record: true
484
+ registers a supported iOS/Android project and records the checked platforms only
485
+ after the report and owned cleanup succeed. Recording waits asynchronously for
486
+ the config lock, honors signal, and rejects unavailable state. Existing remote approval
487
+ checks can refresh their own pending credential records. gc returns the GC
488
+ JSON dry-run report; it never deletes or shuts down idle devices. Report reads
489
+ can refresh their own measured-usage caches. Supplying home or STIM_HOME retains
490
+ the CLI's scoped-home restrictions on machine-global resources.
491
+
492
+ logs({ tail, sources, minLevel, slot, since, grep, errorsOnly }) returns the raw
493
+ stim logs --json records, including native crash capture, agent actions and Expo
494
+ error context. It runs in process, with asynchronous chunked reads and bounded
495
+ record selection when tail is supplied. A new file generation gets a compact
496
+ metadata index; later queries reuse it and index only appended bytes.
497
+ Matching agent actions are also selected within tail; attribution still scans
498
+ each retained session generation to establish device ownership.
499
+ followLogs(options, onRecord) emits the requested backfill followed by new records
500
+ and returns a stop function. AbortSignal also stops the subscription.
501
+ An optional third callback, onBackfill(records), receives initial records instead
502
+ of onRecord. Its promise is awaited before reading another batch; with that
503
+ callback and no tail, the timeline is selected in batches of at most 500.
504
+ Appends still go to onRecord.
505
+ Agent attribution still retains its matching records during this initial read.
506
+ Local appends use filesystem hints with a ten-second repair scan; while a
507
+ directory watch is unavailable, attachment and reads retry every 500 ms.
508
+ Attachment also schedules one 500 ms recovery scan for watch startup.
509
+ Expo context uses a settle timer only while an error is pending. The separate
510
+ agent-action cursor and active hosted log queries keep their current sampling.
438
511
 
439
512
  run takes platform plus its options:
440
513
  ios: configuration, scheme, deviceType, runtime
@@ -449,6 +522,12 @@ build produces an artifact without acquiring a device, starting a runtime, or
449
522
  stopping an existing session. Its result is inferred from platform:
450
523
  const result = await stim.build({ platform: 'ios' });
451
524
  console.log(result.facts.appPath);
525
+ console.log(result.facts.links.workspace);
526
+
527
+ Successful builds include facts.links.workspace, an HTTPS link to this workspace
528
+ on this Stim home. Agents can share it with an already-paired phone or Desktop,
529
+ including when Desktop is not installed on the build machine. Opening it only
530
+ navigates; it does not install or launch the built app.
452
531
 
453
532
  Build options:
454
533
  ios: scheme, configuration, arch (arm64, x86_64, all), buildCache, remoteBuild
@@ -470,16 +549,34 @@ macOS facts include bundle, bundleId, executable, pid, build and launched.
470
549
  Web facts contain the owned browser's existing launch and page facts.
471
550
  Do not interpret 'bundling' or 'unverified' as proven application readiness.
472
551
 
552
+ reload({ platform?, signal?, beforeReload? }) requests a JavaScript reload in
553
+ the live iOS/Android app or reloads the owned Chrome page. It returns the flat
554
+ stim reload --json facts: platform, deviceId, deviceName, appId, metroPort,
555
+ strategy, targets and reverseRestored. Choose ios, android or web when more
556
+ than one platform is live. A request does not prove completion; verify the UI
557
+ and inspect logs. A Metro broadcast can reach other connected clients.
558
+ beforeReload(workspace) runs once after resolving the workspace and before
559
+ usage recording or dispatch; throwing refuses the action. Reload uses the
560
+ 60 second execution and 35 second cleanup budgets described above, including
561
+ STIM_REPORT_TIMEOUT and STIM_CLAIM_UNAVAILABLE. Cancellation stops pending
562
+ probes and dispatch, but cannot retract a reload already sent. It never retries.
563
+
473
564
  stop({ slot? }) returns { ok, outcomes, summary }. It acts on the workspace,
474
565
  including resources from earlier runs. Use a dedicated workspace in CI.
475
- Cancellation waits for the operation worker to exit; stop with a fresh signal
566
+ Cancellation drains the operation's owned work, a read kills its child
567
+ processes, and both reject with STIM_CANCELLED. Cancelling stop while it waits
568
+ for another native operation ends that wait. Once teardown starts, stop finishes
569
+ cleanup before rejecting with STIM_CANCELLED. Its config, workspace-state, and lease lock waits are asynchronous.
570
+ Stop with a fresh signal
476
571
  after cancellation or partial failure. Always inspect cleanup.ok.
477
572
 
478
573
  diagnostics({ tail: 200, errors: false }) returns { directory, records } from
479
574
  the local timeline, even before a successful run. tail: 0 returns paths only.
480
575
  It does not fetch remote logs or capture new crashes. Stim failures reject with
481
576
  StimError carrying code, message, remedy and details (including the log path).
482
- An exception thrown by onProgress cancels its worker and propagates unchanged.
577
+ An exception thrown by onProgress stops further progress and propagates
578
+ unchanged: build or run is cancelled, and stop or a read
579
+ finishes its work first.
483
580
 
484
581
  Normal ownership, device creation, cache locks and coordination remain active.
485
582
  All concurrent artifact-cache writers must share the same coordinating home.
@@ -667,10 +764,10 @@ leased until <time>" for each one.`,
667
764
  agentDevice { stateDir }: absolute workspace agent-device state path;
668
765
  set AGENT_DEVICE_STATE_DIR to it (see guide logs)
669
766
  alreadyRunning true when nothing needed starting
670
- links { desktop }: a stim-desktop://workspace?path=<workspace>
671
- link that shows this workspace in Stim Desktop. Absent when
672
- no app on this Mac opens stim-desktop links. Plain output
673
- prints it on stderr as "Open in Stim Desktop: <link>"; so
767
+ links { desktop }: an HTTPS link containing this Stim home and
768
+ workspace IDs, opened by Desktop here or an already-paired phone. Absent when
769
+ no app on this machine opens stim-desktop links. Plain output
770
+ prints it on stderr as "Open in Stim: <link>"; so
674
771
  does worktree warm, which has no JSON payload
675
772
 
676
773
  stim macos --json
@@ -733,7 +830,7 @@ leased until <time>" for each one.`,
733
830
  configuration is part of it: -release-sim vs
734
831
  -debug-sim-arm64). A single-architecture simulator build
735
832
  ends in its architecture (-arm64 or -x86-64): a local Debug
736
- build compiles this Mac's, a --remote build the remote
833
+ build compiles this machine's, a --remote build the remote
737
834
  host's. A local non-Debug simulator build compiles every
738
835
  architecture the project lists and has no suffix
739
836
  With --eas-profile, fingerprint is computed by EAS CLI using the selected
@@ -756,7 +853,7 @@ leased until <time>" for each one.`,
756
853
  looked up", which is a different fact from "nothing was found"
757
854
  buildMachine selected auto, local or machine name, including cache hits
758
855
  builtOn here or machine name when a build ran; absent otherwise
759
- offloadedTo only on an app a remote Mac compiled: its
856
+ offloadedTo only on an app a remote machine compiled: its
760
857
  remote.machines entry (see \`guide settings\`). cacheHit is
761
858
  false for it. The Android payload carries it too
762
859
  offloadFallback only on an app compiled here after the run considered
@@ -909,7 +1006,7 @@ leased until <time>" for each one.`,
909
1006
  sets its own CMake compiler launcher, or a Gradle run whose
910
1007
  native work was all up to date. None of the three is an
911
1008
  error, and this field is separate from cacheHit. On an
912
- offloaded APK it is the remote Mac's ccache
1009
+ offloaded APK it is the remote machine's ccache
913
1010
  logs the workspace log directory
914
1011
  agentDevice { stateDir }: absolute workspace agent-device state path
915
1012
  durationMs wall time for the whole run
@@ -1010,21 +1107,39 @@ leased until <time>" for each one.`,
1010
1107
  freedMb, and is empty while under budget
1011
1108
  deviceHosts one { machine, state, dnsName?, deviceId?, requestedAt?, expiresAt?,
1012
1109
  host? } per remote.machines entry. state is "approved",
1013
- "pending", "not-asked", "revoked", "lapsed", "node-changed", "not-on-tailnet",
1014
- "tailscale-off", "unreachable", "invalid",
1015
- "credentials-unavailable" or "busy". Tokens stay private.
1016
- Only --fix asks for access or forgets removed names.
1017
- A remote Mac approved for device hosting does not imply
1110
+ "pending", "not-asked", "revoked", "lapsed", "key-changed", "outdated",
1111
+ "not-on-tailnet", "tailscale-off", "unreachable", "invalid",
1112
+ "credentials-unavailable" or "busy". Setup tickets stay private.
1113
+ Only --fix asks for access or forgets removed names. A
1114
+ peer that Tailscale reports as running an OS other than
1115
+ macOS is never asked or contacted and reports
1116
+ "not-asked" with no finding, even with a saved approval,
1117
+ since it can never host.
1118
+ A remote machine approved for device hosting does not imply
1018
1119
  build approval; see \`guide settings\`.
1019
1120
  host is { name, screenRecording, accessibility }, present
1020
1121
  only for approved machines whose worker reports it.
1021
- expiresAt is when a pending request lapses; "lapsed" is
1022
- a pending request whose expiry passed. --fix asks again.
1122
+ expiresAt is when a pending request's setup ticket
1123
+ expires, absent for a Stim Desktop ticket; "lapsed" is a
1124
+ pending request whose expiry passed. --fix asks again.
1125
+ "key-changed" means the worker did not prove the
1126
+ stim-server key pinned at approval. "outdated" means its
1127
+ stim-server speaks protocol 1; the finding names the
1128
+ stim-server service update command to run there.
1129
+ toolchain { entries, projectNode } on macOS, else null. entries is one { name, value,
1130
+ source, from? } for node, ANDROID_HOME, JAVA_HOME, stim,
1131
+ brew and adb: source "environment" when this process's
1132
+ environment provides it, "discovered" when stim-server's
1133
+ toolchain discovery would find it (from says where), or
1134
+ "missing". projectNode is { pin, spec, bin, version } when
1135
+ .nvmrc, .node-version, .tool-versions or package.json volta
1136
+ pins Node (bin and version are null when no install
1137
+ satisfies the pin), else null
1023
1138
  remoteMachines one { machine, state, dnsName?, deviceId?, requestedAt?,
1024
1139
  expiresAt?, offloadable?, reasons?, problems?, capacity?, host? } per
1025
1140
  remote.machines entry, for its build approval; state is "approved", "pending", "not-asked",
1026
1141
  "revoked" (revoked or denied), "lapsed" (the request lapsed
1027
- before approval), "node-changed",
1142
+ before approval), "key-changed", "outdated",
1028
1143
  "not-on-tailnet", "tailscale-off", "unreachable" or
1029
1144
  "invalid". host has the same shape and presence rule as in
1030
1145
  deviceHosts, as do expiresAt (a pending request's lapse
@@ -1033,13 +1148,15 @@ leased until <time>" for each one.`,
1033
1148
  simulator unless --platform android, Android emulator when
1034
1149
  --platform android or the app has android/ or uses Expo,
1035
1150
  plus macOS SwiftPM when Package.swift exists and no platform
1036
- is selected on a Mac), and reasons, each reason it would not,
1151
+ is selected on a Mac; a platform the machine cannot build at
1152
+ all, such as iOS on Linux, is left out while it can build
1153
+ another), and reasons, each reason it would not,
1037
1154
  prefixed "iOS: ", "Android: " or "macOS: " when only one platform
1038
1155
  has it, with a finding
1039
1156
  per reason (code build-machine-<reason>: unreachable,
1040
1157
  checkout, stim-build, arch, xcode, simulator-sdk, macos-sdk,
1041
1158
  cocoapods, bundler, runtime, jdk, android-sdk, ndk,
1042
- build-tools, compile-sdk, disk or busy). problems lists the same
1159
+ build-tools, compile-sdk, disk, busy or platform). problems lists the same
1043
1160
  reasons as { code, reason } with that code. capacity
1044
1161
  is the machine's offer: { running, max, diskFreeBytes,
1045
1162
  minDiskFreeBytes, cpus?, loadPerCore?, builds?, maxBuilds?,
@@ -1050,7 +1167,7 @@ leased until <time>" for each one.`,
1050
1167
  memory-culprit is a costs-time finding naming a process
1051
1168
  with an abnormally large footprint and, when it is safe to
1052
1169
  restart, the command
1053
- 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 Macs > Add
1170
+ 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
1054
1171
 
1055
1172
  ON FAILURE
1056
1173
  \`start\`, \`ios\`, \`android\` and \`web\` all print the error contract instead,
@@ -1067,8 +1184,8 @@ ON FAILURE
1067
1184
 
1068
1185
  An \`ios\` or \`android\` run that \`stim stop\` or Ctrl-C interrupted
1069
1186
  reports STIM_CANCELLED and exits 130. A
1070
- run that exits on the interrupt at once (a second SIGINT, a physical-device
1071
- lease, Ctrl-C with no build tool running) exits 130 with no payload.
1187
+ run that exits on the interrupt at once (a second Ctrl-C, or Ctrl-C with no
1188
+ build tool or SimSlim to stop) exits 130 with no payload.
1072
1189
 
1073
1190
  \`stop --json\` prints { root, ok, supervisor, collectors, metro, device,
1074
1191
  port, metroTunnel, releasedLeases, macos? }. macos is { status: stopped | failed,
@@ -1267,7 +1384,9 @@ RULES
1267
1384
  deviceRecord,
1268
1385
  workspaceOutputs, recording, workspaceDirectory, project,
1269
1386
  buildLock, buildSlot, deviceLease, easSession, worktree,
1270
- cache.
1387
+ cache, gradleVersion, tempLeftover (one "done" labelled
1388
+ with the TMPDIR it cleared, plus a "kept" or "failed" per
1389
+ entry gc checked again or could not delete).
1271
1390
  label is a device, path or cache name; id is the UDID,
1272
1391
  AVD name or path behind it, or null. Under --cache
1273
1392
  parked, a "done" parkedDevice was erased and stays
@@ -1316,7 +1435,7 @@ RULES
1316
1435
  whole workspace directory
1317
1436
  linkedWorktrees { path, idleDays, mergedInto, pullRequest,
1318
1437
  pullRequestUnknown, willRemove, reason,
1319
- detail, eligibleAt } mergedInto is the
1438
+ detail, eligibleAt, buildState } mergedInto is the
1320
1439
  default branch HEAD is merged into
1321
1440
  ("origin/main"), or null. pullRequest is the
1322
1441
  branch's pull request { number, state:
@@ -1327,8 +1446,33 @@ RULES
1327
1446
  origin/main", "PR #12 closed", "idle 9d") or
1328
1447
  kept. eligibleAt is the ISO time a
1329
1448
  recent-activity worktree becomes removable,
1330
- else null. Without --worktrees, the source
1331
- checkout and roots outside git are left out
1449
+ else null. buildState is { bytes, dirs:
1450
+ { path, bytes } } for the in-tree build and
1451
+ dependency directories automatic maintenance
1452
+ clears (ios/build, android/app/build,
1453
+ android/build, android/.gradle, .build,
1454
+ node_modules, ios/Pods, at the root, a
1455
+ project root, or apps/* and packages/* for
1456
+ build directories) and a root .pnpm-store,
1457
+ paths relative to the worktree, largest
1458
+ first; du counts APFS clones in full. A bytes
1459
+ is null when du could not measure it within
1460
+ 10 seconds; sizes are cached for up to an
1461
+ hour while no entry of the directory was
1462
+ added or removed. null
1463
+ for a root outside git. Without --worktrees,
1464
+ the source checkout and roots outside git
1465
+ are left out
1466
+ unlistedWorktreeDirectories
1467
+ { path, repository, bytes, remedy } a
1468
+ directory directly under a repository's
1469
+ .worktrees/ that \`git worktree list\` does
1470
+ not show and that has no .git, or a .git file
1471
+ whose gitdir is gone; a worktree of another
1472
+ repository is left out. bytes is null when du
1473
+ fails. Report-only, never deleted: it may
1474
+ hold work. remedy is the rm -rf command to
1475
+ run after checking it
1332
1476
  parkedSimulators { udid, name, model, runtime, parkedAt, app,
1333
1477
  bytes, listed } app is the bundle id recorded
1334
1478
  at park, else null; with --older-than, only
@@ -1351,6 +1495,20 @@ RULES
1351
1495
  for AVD data with no registration)
1352
1496
  staleDevices { kind, id, name, project, slot, idleDays,
1353
1497
  bytes } only with --older-than
1498
+ finishedDevices { kind, id, name, project, slot, bytes, reason }
1499
+ owned devices of a workspace whose linked
1500
+ worktree gc keeps although its branch is
1501
+ merged or its pull request ended; --delete
1502
+ deletes them unless the workspace is in use,
1503
+ leased, driven, locked, pinned by
1504
+ maintenance.keep or used within
1505
+ gc.worktreeGraceMinutes
1506
+ ownedDeviceDisk { kind, count, bytes, unmeasured } disk of the
1507
+ owned simulators and emulators of registered
1508
+ workspaces, per platform; unmeasured counts the
1509
+ ones whose size could not be read; parked
1510
+ devices are under parkedSimulators and
1511
+ parkedEmulators
1354
1512
  staleDeviceRecords { kind, id, project, slot } --delete clears
1355
1513
  the record only
1356
1514
  staleLedgerEntries { kind: "ios" | "android" | "web", id }
@@ -1360,6 +1518,11 @@ RULES
1360
1518
  root, and browser profile paths whose
1361
1519
  directory is gone; --delete forgets the
1362
1520
  ledger entry only
1521
+ staleEasLedgerEntries { id, workspaceRoot } entries of the machine
1522
+ EAS session ledger whose id is not a UUID and
1523
+ whose absolute workspace root is missing on a
1524
+ mounted volume; --delete forgets the entry
1525
+ only and contacts no EAS session
1363
1526
  staleStatusCaches { kind: "disk-usage" | "pull-request", path,
1364
1527
  file, bytes } entries of $STIM_HOME/disk-usage
1365
1528
  and pull-requests whose path is gone from a
@@ -1407,6 +1570,51 @@ RULES
1407
1570
  null with any --cache scope
1408
1571
  swiftpmCache <SwiftpmCacheUsage|null> report-only, never deleted;
1409
1572
  null with any --cache scope
1573
+ gradleVersions { home, olderThanDays, blocked, gradleRunning,
1574
+ gradleRunningPids, versions: [{ version, dirs,
1575
+ bytes, lastModifiedAt, willDelete, reason,
1576
+ detail }], leftovers } | null; only with
1577
+ --cache gradle-versions. blocked is null or
1578
+ { reason, detail } with machine-global,
1579
+ no-registry, unsafe-layout, listing-failed,
1580
+ unreadable-project, gradle-running or
1581
+ processes-unknown; gradleRunning is the
1582
+ gradle-running detail or null and
1583
+ gradleRunningPids the pids behind it; a
1584
+ version's reason is null, one of those,
1585
+ not-a-directory, walk-failed, too-many-entries
1586
+ or recently-modified. leftovers lists
1587
+ .stim-delete-* directories --delete finishes
1588
+ removing
1589
+ machineCaches { measuredAt, complete, notices, gradle, simRuntimes,
1590
+ deviceSupport, runnerBuilds } | null
1591
+ report-only, never deleted; null with any
1592
+ --cache scope. gradle: { home, unusedVersions:
1593
+ [{ version, bytes, dirs, remedy }],
1594
+ usedVersions: [{ version, projects }],
1595
+ unreadableProjects, modules }. simRuntimes:
1596
+ { checked, runtimes: [{ identifier,
1597
+ runtimeIdentifier, version, build, bytes,
1598
+ deletable, lastUsedAt, devices, remedy }] }.
1599
+ deviceSupport: { entries: [{ dir, bytes }],
1600
+ remedy }. runnerBuilds: { agentDeviceVersion,
1601
+ xcodeBuildVersion, groups: [{ packageVersion,
1602
+ xcodeBuildVersion, current, entries, inUse,
1603
+ bytes, oldestUseAt }], remedy }. Sizes and
1604
+ versions come from
1605
+ $STIM_HOME/machine-cache-usage.json, at most
1606
+ 10 minutes old
1607
+ tempLeftovers { root, groups: [{ kind, count, bytes,
1608
+ deleteCount, deleteBytes, kept: [{ reason,
1609
+ count }], remedy }] } | null recognized
1610
+ entries directly in TMPDIR (root, resolved).
1611
+ kind is stim (stim-claim-*, stim-host-*,
1612
+ stim-fakehome-*, stim-hosted-macos-*,
1613
+ stim-server-*, stim-ws-* directories), xctrace
1614
+ (instruments*.ktrace) or jest (Jest's
1615
+ jest_<uid in base 36> cache, never deleted). deleteCount
1616
+ is what --delete removes. null with STIM_HOME
1617
+ set or any --cache scope
1410
1618
  caches { name, dir, source, bytes, note, willEmpty,
1411
1619
  emptySkipped, scopedEmpty } alive, not
1412
1620
  garbage; willEmpty marks the ones --delete
@@ -1449,7 +1657,8 @@ RULES
1449
1657
  stim-server
1450
1658
  linkedWorktrees not-a-worktree | bare-repository |
1451
1659
  source-checkout-unknown | source-checkout |
1452
- locked | in-use | status-unreadable | dirty |
1660
+ locked | nested-worktree | in-use |
1661
+ status-unreadable | dirty |
1453
1662
  unpushed-unchecked | unpushed | submodules |
1454
1663
  not-merged | merge-unknown | last-use-unknown |
1455
1664
  recently-used | activity-unknown |
@@ -1506,6 +1715,8 @@ RULES
1506
1715
  absent until it has run.
1507
1716
  recording { enabled }: whether stim-server may record the workspace's
1508
1717
  device screens for replay, from recording.enabled
1718
+ hidden true when workspace.hidden is set; absent otherwise. The next
1719
+ command that uses the workspace clears it.
1509
1720
 
1510
1721
  Each environment also carries stage, where the workspace is as Stim
1511
1722
  Desktop and the phone app show it: { kind, since, platform, closedApps }.
@@ -1791,7 +2002,7 @@ RULES
1791
2002
  device: its boot, adoption cleanup, or a physical
1792
2003
  device's lease and connection check. A boot that
1793
2004
  finishes during the build adds no device time. For a
1794
- device hosted on another Mac, device covers reserving
2005
+ device hosted on another machine, device covers reserving
1795
2006
  and preparing it there, install covers delivering the
1796
2007
  app, and launch starts when the host launches it. An
1797
2008
  --eas-profile run has no cache lookup, so its outcome
@@ -1933,7 +2144,7 @@ RULES
1933
2144
  lookup, and create no lastBuild record or failed-run stats.
1934
2145
  buildMachine selected auto, local or configured machine entry; always on new build records
1935
2146
  builtOn here or machine name; absent when no build ran (cache/refusal)
1936
- offloadedTo only on a run a remote Mac compiled: its name
2147
+ offloadedTo only on a run a remote machine compiled: its name
1937
2148
  offloadFallback
1938
2149
  only on a run that considered offloading and built here: one
1939
2150
  reason per machine when none took the build, or why the
@@ -2173,7 +2384,7 @@ RULES
2173
2384
  baseline. rekeyedBy is empty.
2174
2385
  placement with ios.remote or android.remote set to auto or a Mac,
2175
2386
  where the plan assumes the device runs: "on <machine>" or
2176
- "this Mac; auto may use <machines>"; absent otherwise
2387
+ "this machine; auto may use <machines>"; absent otherwise
2177
2388
  refusal { code, message, remedy } when the run would refuse:
2178
2389
  STIM_PREBUILD_FAILED for a tracked native dir the fingerprint
2179
2390
  leaves out, STIM_EAS_BUILD_MISSING for an EAS miss
@@ -2205,7 +2416,7 @@ RULES
2205
2416
  A bucket carries runs, failed, hits, misses, coldRuns, coldRunMs, hitRuns,
2206
2417
  hitRunMs, timeSavedMs, firstRunAt and lastRunAt, plus lastColdBuildMs and
2207
2418
  lastPodsMs once the project has compiled or installed pods, and
2208
- offloadedRuns, offloadedRunMs and lastOffloadHost once a remote Mac
2419
+ offloadedRuns, offloadedRunMs and lastOffloadHost once a remote machine
2209
2420
  compiled one. An offloaded run is a miss but not a cold run, so the cold
2210
2421
  average, time saved and build estimates stay local. Milliseconds are
2211
2422
  integers.
@@ -2325,10 +2536,10 @@ BUILD PLACEMENT (\`offload\`)
2325
2536
  decision, reason, machine?, buildMs?, slotWaitMs?, deviceSlotWaitMs?, localEstimateMs?, failed? }.
2326
2537
  slotWaitMs and deviceSlotWaitMs are whole milliseconds waiting for build
2327
2538
  and owned-device slots respectively, present only when positive.
2328
- decision is "here", "offloaded", or "fell-back" (it tried a remote Mac
2539
+ decision is "here", "offloaded", or "fell-back" (it tried a remote machine
2329
2540
  and built here). reason is why: the run's \`placement:\` reason, such as
2330
2541
  "load 0.6/core, 1 of 3 build slots busy here" (auto keeps the build here
2331
- while this Mac has room) or "remote.buildMode is off" (which prints no line),
2542
+ while this machine has room) or "remote.buildMode is off" (which prints no line),
2332
2543
  or for a fallback the cause, prefixed by the machine it concerns, such as
2333
2544
  "mini: no less loaded (load 1.2/core there, 0.6/core here)". buildMs is the compile here, or the offloaded build's
2334
2545
  total (offer, sync, build and fetch); localEstimateMs is this project's
@@ -2337,7 +2548,7 @@ BUILD PLACEMENT (\`offload\`)
2337
2548
  placements lists them newest first: the last 100 from the last 7 days.
2338
2549
  Per machine, \`total\` keeps { offloaded, offloadedMs, savedMs, fallbacks,
2339
2550
  lastOffloadAt?, lastFallbackAt? } for good and \`today\` counts { offloaded,
2340
- offloadedMs, savedMs, fallbacks } from the placements on this Mac's local
2551
+ offloadedMs, savedMs, fallbacks } from the placements on this machine's local
2341
2552
  calendar day. savedMs sums localEstimateMs minus buildMs over offloaded
2342
2553
  builds that had an estimate, so it is an ESTIMATE and is negative when the
2343
2554
  machine was slower. \`offload.today\` counts the day's placements by
@@ -2375,7 +2586,7 @@ comes from \`start --remote\`, eas/proxy in \`ios.remote\`, or \`android.remote\
2375
2586
  HOSTED IOS METRO
2376
2587
  ios --remote <machine> keeps Metro local and needs no start --remote.
2377
2588
  The supervisor watches private gateway requests in workspace state, binds
2378
- only this Mac's Tailscale address and accepts only the pinned hosting peer
2589
+ only this machine's Tailscale address and accepts only the pinned hosting peer
2379
2590
  with a per-session secret. The host's bridge is loopback-only. Port and secret
2380
2591
  stay in 0600 workspace state and never appear in status, JSON or logs.
2381
2592
  metro.tunnel and metro.publicUrl are ignored. Supervisor restart recreates
@@ -2515,6 +2726,12 @@ WHAT THE SUPERVISOR IS
2515
2726
  launches stay UNVERIFIED: read \`stim logs --source metro\`
2516
2727
  for the bundle lines.
2517
2728
 
2729
+ expo-child and command-child put the Node the app pins (.nvmrc,
2730
+ .node-version, .tool-versions nodejs, or package.json volta.node) first
2731
+ on PATH when an nvm, fnm, Volta, asdf or mise install satisfies the pin
2732
+ and the node already on PATH does not; the metro log then records a
2733
+ project_node event naming it. bare-inproc runs on the supervisor's Node.
2734
+
2518
2735
  In expo-child mode, remote intent plus metro.tunnel "expo" makes \`start\` pass
2519
2736
  \`--tunnel\` and EXPO_UNSTABLE_TUNNEL_V2=1 (the legacy ws-tunnel path is
2520
2737
  locked to port 8081, which every reserved port but the first collides with)
@@ -2822,6 +3039,16 @@ FLAGS
2822
3039
  mode only, on stderr. With --errors, an Expo error record
2823
3040
  also carries context (see THE RECORD).
2824
3041
 
3042
+ Local follow uses filesystem change hints with a ten-second repair scan for
3043
+ missed events or a replaced directory. Until a directory watch is available,
3044
+ it retries and scans every 500 ms, with one recovery scan 500 ms after attachment.
3045
+ On Windows a moved log directory can require the ten-second repair scan.
3046
+ Expo context settling uses a timer only
3047
+ while an error is pending; otherwise idle files are not repeatedly scanned.
3048
+ While following, hosted targets join and leave from local state notifications.
3049
+ Missed notifications are reconciled about every 10 seconds. Active hosts still
3050
+ serve bounded log queries.
3051
+
2825
3052
  --ERRORS, PRECISELY
2826
3053
  Level error or fatal, from metro, client and build, plus device records with
2827
3054
  event native_crash (app/device/time-correlated OS reports or fatal app console output)
@@ -2909,7 +3136,7 @@ FLAGS
2909
3136
  responses, do not cut the stack short. Human output joins the lines to the
2910
3137
  message. --json leaves msg as captured and adds them to the error record as
2911
3138
  a context array of strings; each record is still one line. --follow holds an
2912
- Expo error until a poll, about every 500 ms, adds no more of its lines, so
3139
+ Expo error until a 500 ms settle check adds no more of its lines, so
2913
3140
  it can print up to about a second after Expo does. Non-follow human output
2914
3141
  also shows bare React Native symbolication as separate context, because
2915
3142
  Metro does not provide an error correlation identifier. Context never
@@ -2958,16 +3185,16 @@ failures before a later launch marker are hidden. Machine passes are reported in
2958
3185
  status and maintenance/maintenance.ndjson; this command reads workspace logs.
2959
3186
 
2960
3187
  REMOTE REQUEST RECORDS
2961
- A request to another Mac that fails is a warn record with src: build in the
3188
+ A request to another machine that fails is a warn record with src: build in the
2962
3189
  run's build log: remote_connect_failed { host, capability, ms, timeoutMs, msg,
2963
- code } (could not connect or say hello, e.g. "no reply in time" when the Mac
3190
+ code } (could not connect or say hello, e.g. "no reply in time" when the machine
2964
3191
  did not answer hello) and remote_request_failed { method, ms, code, msg }.
2965
3192
  Timings of requests that succeed are debug records (below).
2966
3193
  stim-server writes to its service log (~/Library/Logs/Stim/<label>.log), with
2967
3194
  no tokens or tickets: "request failed method=<m> client=<device id> run=<runId>
2968
3195
  ms=<n> error=<code>" for an error reply (once a minute per client, method and
2969
3196
  code) and "host_connect ... error=<reason_with_underscores>" when its connection to a hosting
2970
- Mac fails. With debug.logs on or STIM_DEBUG=1 for the server it logs every
3197
+ machine fails. With debug.logs on or STIM_DEBUG=1 for the server it logs every
2971
3198
  request instead: "debug request method=<m> client=<id> run=<runId> ms=<n>
2972
3199
  slow=true error=<code> whois=<ms> probe=<ms>" (slow means 1000 ms or more;
2973
3200
  whois and probe are the hello's Tailscale identity lookup and its wait for
@@ -2984,14 +3211,14 @@ stim logs does not show them. Read them with jq:
2984
3211
  jq -c 'select(.event=="exec" and .ms>1000)' ~/.stim/logs/debug/cli.ndjson
2985
3212
  Events: run_start, run_end { exit, ms }, exec { program, ms, ok, exit? }, and
2986
3213
  remote_connect and remote_request { ms, ok, code? } for requests to another
2987
- Mac. They carry no arguments, tokens or tickets.
3214
+ machine. They carry no arguments, tokens or tickets.
2988
3215
 
2989
3216
  Placement records use src: placement and sit in the run's build log
2990
3217
  (build-*.ndjson), so a new run replaces the previous run's. A run writes one
2991
- record per decision it makes (a build that compiles and has a remote Mac to
3218
+ record per decision it makes (a build that compiles and has a remote machine to
2992
3219
  consider, or ios/android --remote auto), with event build_placement,
2993
3220
  device_placement or placement_fallback. A cache hit, a project with no paired
2994
- remote Mac and a named --remote target write none:
3221
+ remote machine and a named --remote target write none:
2995
3222
  stim logs --source placement
2996
3223
  stim logs --source placement --json
2997
3224
  Fields: kind (build or device), platform, settings [{ key, value, from }] with
@@ -2999,7 +3226,7 @@ from flag, env, setting or default (remote.build and remote.buildMode for a
2999
3226
  build, ios.remote or android.remote, plus remote.easFallback when it is on, for
3000
3227
  a device), candidates [{ machine, code,
3001
3228
  msg, detail? }], choice { machine, code, msg } with machine local when the run
3002
- stays on this Mac, and fallback { code, msg, machine? } when a remote Mac that
3229
+ stays on this machine, and fallback { code, msg, machine? } when a remote machine that
3003
3230
  was meant to take the run did not (level warn). The msg is the same text the
3004
3231
  placement: and build: phase lines print.
3005
3232
  Candidate codes: accepted, unreachable, busy, disk, load, version-mismatch
@@ -3197,6 +3424,13 @@ WHAT WRITES WHAT
3197
3424
  };
3198
3425
  //#endregion
3199
3426
  //#region src/guide/errors.ts
3427
+ const PROVISION_CONTEXT = `\`stim device provision ios [udid]\` is the one Stim command that changes an Apple
3428
+ Developer account: it registers the phone with the project's team and creates
3429
+ or refreshes the iOS App Development profile (and, when the keychain holds
3430
+ none, an Apple Development certificate) by running xcodebuild with
3431
+ -allowProvisioningUpdates -allowProvisioningDeviceRegistration. No build, and
3432
+ no \`ios --device\` run, ever does this. Agents run it only when the user
3433
+ explicitly asks for this step.`;
3200
3434
  const errors = {
3201
3435
  summary: "Every refusal Stim can print: an index of codes, and one section for each",
3202
3436
  sectionHint: "<CODE>",
@@ -3208,10 +3442,11 @@ Branch on the code, never on the message.`,
3208
3442
  STIM_HOSTING_REFUSED: {
3209
3443
  summary: "a hosting Mac refused or could not confirm its native session; no fallback after reservation",
3210
3444
  body: () => `STIM_HOSTING_REFUSED
3211
- The message names the hosting Mac and its reason: unreachable or changed
3212
- tailnet node, no installed simulator or emulator choice, no capacity, elevated or unknown
3445
+ The message names the hosting Mac and its reason: unreachable, a changed
3446
+ server key, a stim-server too old to speak protocol 2 (stim doctor names
3447
+ the update command), no installed simulator or emulator choice, no capacity, elevated or unknown
3213
3448
  memory pressure, or an unresolved reservation or delivery. Stim boots nothing
3214
- locally and never tries another Mac after choosing a named host or creating a
3449
+ locally and never tries another machine after choosing a named host or creating a
3215
3450
  hosted session. Auto skips declined offers before building, then uses the
3216
3451
  local queue if none admits. A reserve refusal after an accepted offer fails
3217
3452
  with this code and asks to retry; it does not re-place the built architecture.
@@ -3229,7 +3464,7 @@ Branch on the code, never on the message.`,
3229
3464
  on the host to enable handoff and native logs (hello features hosted-ios-data
3230
3465
  for iOS and hosted-android-data for Android).
3231
3466
  A name outside remote.machines is STIM_BAD_ARG. Missing or pending hosting
3232
- approval keeps the doctor --fix and stim-server devices grant remedies.`
3467
+ approval keeps the doctor --fix and stim-server setup remedies.`
3233
3468
  },
3234
3469
  STIM_EAS_BUILD_MISSING: {
3235
3470
  summary: "no completed EAS development build matches; build only with session authorization",
@@ -3333,7 +3568,7 @@ Branch on the code, never on the message.`,
3333
3568
  the source checkout, then warm again.`
3334
3569
  },
3335
3570
  STIM_BUILD_FAILED: {
3336
- summary: "xcodebuild or gradle failed; the Android missing-SDK and APK refusals; a damaged compilation-cache object",
3571
+ summary: "xcodebuild or gradle failed; the Android missing-SDK and APK refusals",
3337
3572
  body: () => `STIM_BUILD_FAILED
3338
3573
  xcodebuild or gradle failed. The EXTRACTED diagnostics are printed (capped),
3339
3574
  not the transcript. Read the log path on the next line for the rest.
@@ -3360,17 +3595,40 @@ Branch on the code, never on the message.`,
3360
3595
  reports UP-TO-DATE and repackages nothing. Stim installs it. Gradle owns task
3361
3596
  freshness and the fingerprint owns cache freshness; Stim does not second-guess
3362
3597
  either from the file's mtime.
3363
-
3364
- "failed to scan dependencies for source ..." on pods you did not touch (ios)
3365
- The compilation cache holds a damaged object. Xcode reports it per source
3366
- file, so it names whichever targets reach the object first -- often pods such
3367
- as sqlite3, nanopb or libwebp -- and the list changes between runs. The
3368
- transcript carries the cause:
3598
+ A damaged compilation-cache object stays STIM_BUILD_FAILED when Stim did
3599
+ not give the build its shared cache; STIM_COMPILATION_CACHE_CORRUPT lists
3600
+ the messages and the gc remedy.`
3601
+ },
3602
+ STIM_COMPILATION_CACHE_CORRUPT: {
3603
+ summary: "an iOS build failed on a damaged shared compilation cache; Stim emptied it, rerun the command",
3604
+ body: () => `STIM_COMPILATION_CACHE_CORRUPT (ios)
3605
+ The build used the shared compilation cache ($STIM_HOME/compilation-cache)
3606
+ and the compiler reported a damaged object in it. A cache write that a full
3607
+ disk or a killed build cut short leaves such an object, and every later build
3608
+ that reaches it fails the same way. Stim matches only the compiler's own
3609
+ corruption errors in the transcript:
3610
+ LLVM ERROR: OnDiskCAS: corrupt internal reference
3611
+ error: CAS operation failed: ...
3369
3612
  error: CAS-based dependency scan failed: not a IncludeTreeRoot node kind
3370
- A cache write that a full disk or a killed build cut short leaves such an
3371
- object, and upgrading the CLI does not clear it. Empty that one cache with
3372
- \`gc --delete --cache "compilation cache"\`, then build again. The next
3373
- build is a cold one.`
3613
+ Xcode reports them per source file, so the failing targets are whichever
3614
+ reach the object first -- often pods you did not touch -- and they change
3615
+ between runs.
3616
+ Stim empties the whole cache at once, the way
3617
+ \`gc --delete --cache "compilation cache"\` does, and fails the build. It
3618
+ does not retry. RUN THE SAME COMMAND AGAIN: that build starts with an empty
3619
+ cache, so it is a cold one. The emptying is recorded in the maintenance log.
3620
+ Every iOS build that compiles with the cache holds a shared claim on it,
3621
+ <cache>.claims beside the cache directory, while xcodebuild runs. Stim
3622
+ empties the cache only under that set's exclusive claim, here, in gc and in
3623
+ maintenance. When another build holds it or its claim is unresolved, Stim
3624
+ leaves the cache alone, because that build may be using it, and the message
3625
+ says so. Once nothing else is building, run
3626
+ \`gc --delete --cache "compilation cache"\`, then run the command again.
3627
+ A build on a build worker gets the same handling on the worker, which
3628
+ empties its own cache for that client. With --remote-build <machine> the
3629
+ build fails with this code and the worker's message; otherwise it falls
3630
+ back to building here, and the fallback line names this code. Xcode builds
3631
+ run outside Stim take no claim, so a cache they share is not guarded.`
3374
3632
  },
3375
3633
  STIM_PATH_TOO_LONG: {
3376
3634
  summary: "Windows only: the project root leaves no room for the NDK object paths ninja must open",
@@ -3380,12 +3638,17 @@ Branch on the code, never on the message.`,
3380
3638
  after their mangled absolute source path, so each carries the project root
3381
3639
  twice unless CMake shortens it, and CMake only shortens a name that then
3382
3640
  fits the object path limit Stim's Gradle shim sets. The message names the
3383
- longest known object (measured on the e2e fixture, for the ABI being
3384
- built) and the longest root it leaves room for. Refused BEFORE Gradle runs
3385
- -- a cache hit still installs -- because the build would otherwise fail
3386
- deep inside ninja ("mkdir ... No such file or directory" or "Filename
3387
- longer than 260 characters"). Map the project to a drive letter (\`subst
3388
- X: <root>\`, then run Stim from X:\\) or move it under a shorter root.
3641
+ longest known object for the ABI being built and the longest root it
3642
+ leaves room for. A project with react-native-gesture-handler 3, whose
3643
+ shared shadow nodes have the longest known object path, fits a root of at
3644
+ most 24 characters for an x86_64 Debug build and 21 for arm64-v8a; other
3645
+ projects fit 51 and 48. Refused BEFORE Gradle
3646
+ runs -- a cache hit still installs -- because the build would otherwise
3647
+ fail deep inside ninja ("mkdir ... No such file or directory" or
3648
+ "Filename longer than 260 characters"). Move or clone the project under a
3649
+ short real path such as C:\\src\\<name> and run Stim there. A subst drive
3650
+ does not help: Node's realpath and Expo autolinking resolve it back to the
3651
+ long path, and Gradle then sees two roots.
3389
3652
  A custom Gradle buildStagingDirectory can be shorter than the default; this
3390
3653
  preflight does not inspect that configured path and may refuse it too.
3391
3654
  \`doctor --platform android\` reports the same root as a finding for the
@@ -3527,7 +3790,7 @@ Branch on the code, never on the message.`,
3527
3790
  works. See stim guide macos.`
3528
3791
  },
3529
3792
  STIM_OFFLOAD_REFUSED: {
3530
- summary: "the selected remote Mac or automatic build pool cannot build this app",
3793
+ summary: "the selected remote machine or automatic build pool cannot build this app",
3531
3794
  body: () => `STIM_OFFLOAD_REFUSED
3532
3795
 
3533
3796
  Automatic placement also refuses when local is excluded in remote.buildPoolDisabled
@@ -3537,20 +3800,20 @@ builds are not interrupted and a cache hit needs no compiler.
3537
3800
 
3538
3801
  A named --remote-build selection is strict. The message names the machine
3539
3802
  and why it cannot take or finish the build: not configured or paired, approval
3540
- pending or denied, unreachable or changed pinned identity, incompatible
3803
+ pending or denied, unreachable, changed pinned server key, a stim-server
3804
+ too old to speak protocol 2, incompatible
3541
3805
  toolchain/runtime/CPU, low disk, busy, sync/build failure, artifact failure,
3542
3806
  or a checkout that changed while it built. Unsupported build kinds also refuse.
3543
3807
  No local xcodebuild, Gradle or SwiftPM compile or alternate machine follows a
3544
- refusal. Prebuild and pod install still run on this Mac before a named offload.
3808
+ refusal. Prebuild and pod install still run on this machine before a named offload.
3545
3809
  Unlisted or unpaired names refuse at setup before consulting the cache, without
3546
3810
  a build record or failed-run stats. A listed paired name with a cache hit needs
3547
3811
  no build and does not contact the selected machine.
3548
3812
 
3549
3813
  Run stim doctor --fix to ask for build access if not paired. Check
3550
- stim settings get remote.machines. A person on the worker finds the id with
3551
- stim-server devices and approves this Mac with stim-server devices grant <id> --build,
3552
- or runs stim-server setup on the worker with its node, ticket and expiry
3553
- (guide settings). Agents never run setup or approve requests.
3814
+ stim settings get remote.machines. A person on the worker approves this machine
3815
+ by running the stim-server setup command doctor --fix printed, with its ticket
3816
+ and expiry (guide settings). Agents never run setup or approve requests.
3554
3817
  Rerun with --remote-build auto for normal placement and local fallback, or
3555
3818
  --remote-build local to keep the build here.
3556
3819
  `
@@ -3558,12 +3821,14 @@ Rerun with --remote-build auto for normal placement and local fallback, or
3558
3821
  STIM_WORKTREE_SERVICE: {
3559
3822
  summary: "worktree remove refuses a folder a stim-server service runs from",
3560
3823
  body: () => `STIM_WORKTREE_SERVICE
3561
- A LaunchAgent that \`stim-server service install\` wrote (it carries a
3562
- StimService marker in ~/Library/LaunchAgents/<label>.plist, default label
3563
- dev.stim.server) runs a program from inside this worktree. Removing the
3564
- folder would delete the running server's code. The refusal also covers a
3565
- service that is installed but stopped, because launchd loads it again at the
3566
- next login, and a marked plist Stim could not read. \`--force\` does not
3824
+ A service that \`stim-server service install\` wrote runs a program from
3825
+ inside this worktree: on macOS a LaunchAgent with a StimService marker in
3826
+ ~/Library/LaunchAgents/<label>.plist, on Linux a systemd user unit with an
3827
+ [X-Stim] Managed=true section in ~/.config/systemd/user/<label>.service
3828
+ (default label dev.stim.server). Removing the folder would delete the
3829
+ running server's code. The refusal also covers a service that is installed
3830
+ but stopped, because launchd or systemd starts it again, and a marked file
3831
+ Stim could not read. \`--force\` does not
3567
3832
  override it, and gc leaves the worktree in place.
3568
3833
  Reinstall the service from another build (stim-server service install --label
3569
3834
  <label> from a checkout that stays), or remove it with stim-server service
@@ -3664,10 +3929,11 @@ spending a build or a bundle, that the check can succeed.`,
3664
3929
  The built or cached .app has no embedded.mobileprovision, or
3665
3930
  \`openssl smime\` could not decode the one it has. The first means the build
3666
3931
  produced an unsigned app -- almost always a simulator-sliced artifact.
3667
- Set a team and a Development profile for the target's configuration in
3668
- Xcode > Signing & Capabilities, then BUILD ONCE FROM XCODE to install the
3669
- profile. Stim will not do that step: registering a device or minting a
3670
- profile changes your Apple Developer account, so Stim never passes
3932
+ Set a team for the target's configuration in Xcode > Signing &
3933
+ Capabilities, then run \`stim device provision ios [udid]\` (see
3934
+ STIM_PROVISION_UNCONFIRMED) or BUILD ONCE FROM XCODE to install the profile.
3935
+ No build does that step: registering a device or minting a profile changes
3936
+ your Apple Developer account, so builds never pass
3671
3937
  -allowProvisioningUpdates.`
3672
3938
  },
3673
3939
  STIM_PROFILE_MISMATCH: {
@@ -3685,8 +3951,10 @@ spending a build or a bundle, that the check can succeed.`,
3685
3951
  refuses rather than guessing. Local device runs need a development
3686
3952
  profile;
3687
3953
  - it is a development or ad hoc profile whose device list does not name
3688
- this UDID. Register the UDID at developer.apple.com, regenerate the
3689
- profile, and build once from Xcode.
3954
+ this UDID. Run \`stim device provision ios <udid>\`, which registers
3955
+ the UDID and refreshes the Development profile after asking, or
3956
+ register it at developer.apple.com, regenerate the profile, and build
3957
+ once from Xcode.
3690
3958
  With --eas-profile, follow the EAS device:create and build commands in the
3691
3959
  refusal instead. Registration, signing changes and cloud builds need session
3692
3960
  authorization. See stim guide lifecycle eas.`
@@ -3720,6 +3988,44 @@ spending a build or a bundle, that the check can succeed.`,
3720
3988
  and confirm exactly one identity matches the name. The cache entry itself is
3721
3989
  never modified -- the failure is on a temporary copy, and the run builds
3722
3990
  fresh.`
3991
+ },
3992
+ STIM_PROVISION_UNCONFIRMED: {
3993
+ summary: "device provision ios listed its account changes and nobody confirmed them; nothing changed",
3994
+ separator: "--- iOS PROVISIONING CODES (`stim device provision ios`, and only there) ---",
3995
+ context: PROVISION_CONTEXT,
3996
+ body: () => `STIM_PROVISION_UNCONFIRMED
3997
+ The command printed what it would change -- the team, the phone, each bundle
3998
+ id whose Development profile it would create or refresh, and whether it uses
3999
+ the Xcode account or an App Store Connect API key -- and nobody confirmed:
4000
+ the terminal prompt was answered with anything but y, or there was no
4001
+ terminal (or --json) and no --yes. Nothing changed. \`--json\` adds the plan.
4002
+ Confirm in a terminal, or pass --yes once the account owner agreed. Agents
4003
+ never add --yes on their own.`
4004
+ },
4005
+ STIM_PROVISION_FAILED: {
4006
+ summary: "device provision ios could not provision: no account or key, team not accessible, or project signing",
4007
+ context: PROVISION_CONTEXT,
4008
+ body: () => `STIM_PROVISION_FAILED
4009
+ Before xcodebuild runs: the app has no Xcode project, xcodebuild cannot
4010
+ report the scheme's build settings, a signed target names no development
4011
+ team or uses manual signing (xcodebuild creates profiles only for automatic
4012
+ signing), the temporary DerivedData cannot be created, or a native app uses
4013
+ Swift packages that Stim has not resolved yet (no workspace-state.json in
4014
+ its package directory): run \`stim ios\` or \`stim ios --device <udid>\` once
4015
+ to resolve them, then provision. Packages resolved for an older
4016
+ Package.resolved fail inside xcodebuild, whose error is quoted. The remedy
4017
+ names the Xcode or app-config change. From xcodebuild: "No Accounts" means Xcode has no Apple
4018
+ ID signed in and no App Store Connect key is set -- sign in under Xcode >
4019
+ Settings > Accounts, or set ios.appStoreConnectKeyPath,
4020
+ ios.appStoreConnectKeyId and ios.appStoreConnectIssuerId (stim guide
4021
+ settings); "No Account for Team" means that account or key cannot reach the
4022
+ project's team; a 401 means Apple rejected the key. Any other xcodebuild
4023
+ error is quoted, with the key path replaced. An interrupt or the 15-minute
4024
+ timeout also ends here. Once xcodebuild has run, the refusal still lists
4025
+ any profile it installed and any new keychain identity (\`--json\`:
4026
+ \`changes\`), because Apple may already have registered the phone; running
4027
+ the command again is safe. A phone that is not paired or has Developer Mode
4028
+ off refuses earlier, with STIM_NO_DEVICE.`
3723
4029
  },
3724
4030
  STIM_NO_LAN_ADDRESS: {
3725
4031
  summary: "the Mac has no non-internal IPv4 interface; a tunnel cannot help a phone",
@@ -4385,20 +4691,32 @@ captured" (in metro.ndjson, bare RN)
4385
4691
  \`workspace removal\` -- both are \`stim worktree remove\`). Let it finish,
4386
4692
  then run the command again. \`start --remote\` reports this same case as
4387
4693
  STIM_WORKTREE_REMOVAL_IN_PROGRESS instead.`
4694
+ },
4695
+ STIM_REPORT_TIMEOUT: {
4696
+ summary: "An in-process doctor or GC report exceeded its execution budget",
4697
+ body: () => `STIM_REPORT_TIMEOUT
4698
+
4699
+ The doctor() or gc() API read exceeded its 60 second execution budget. Stim
4700
+ attempts only that report's cleanup before rejecting, with a 35 second cleanup
4701
+ budget. Unfinished cleanup returns STIM_CLAIM_UNAVAILABLE instead.
4702
+ Other clients are unaffected. Retry after the machine is less busy, or narrow
4703
+ the report with doctor({ platform }) or gc({ cache }). No GC deletion or idle
4704
+ shutdown occurs through these report methods.
4705
+ `
4388
4706
  },
4389
4707
  STIM_CANCELLED: {
4390
4708
  summary: "an ios or android run was interrupted by `stim stop` or Ctrl-C before it finished",
4391
4709
  body: () => `STIM_CANCELLED (ios, android; exit 130)
4392
4710
  "The ios run was cancelled by \`stim stop\` (pid <n>) before it finished."
4393
- The run received SIGINT: from \`stim stop\`, which interrupts a build it
4394
- leaves with nothing to deploy to, or from Ctrl-C. It forwarded the interrupt
4711
+ The run was interrupted: by \`stim stop\`, which cancels a build it leaves
4712
+ with nothing to deploy to, or by Ctrl-C. It forwarded the interrupt
4395
4713
  to the build tool it was running (xcodebuild, Gradle, pod install, expo
4396
4714
  prebuild) and failed through its own cleanup; the next line names the step it
4397
4715
  stopped at. An interrupted build stores no artifact, so the next run looks
4398
4716
  up the cache as usual. Stopped between build tools, a run that \`stop\`
4399
4717
  cancelled starts no further build tool and stops before it installs. A
4400
- second SIGINT exits at once, and so does a run that holds a physical-device
4401
- lease; those exits print no payload. Run the same command again when you want the
4718
+ second Ctrl-C exits at once, and so does a Ctrl-C with no build tool or
4719
+ SimSlim to stop; those exits print no payload. Run the same command again when you want the
4402
4720
  app on a device.`
4403
4721
  },
4404
4722
  STIM_STOP_BLOCKED: {
@@ -4409,6 +4727,10 @@ captured" (in metro.ndjson, bare RN)
4409
4727
  - "... did not exit within 60s of SIGINT": the run was interrupted but is
4410
4728
  still running. Wait for it, or end it with the \`kill <pid>\` the remedy
4411
4729
  prints, then run \`stim stop\` again.
4730
+ - "... did not exit within 60s of the stop request": the run is inside a
4731
+ process that embeds Stim, such as stim-server, and runs other work too.
4732
+ Stop does not signal that process. Wait for the run, or cancel it from
4733
+ the client that started it, then run \`stim stop\` again.
4412
4734
  - "... pid <n> is no longer that run; the build tool it started (pid <m>)
4413
4735
  still holds the claim": the run itself died and its build tool keeps the
4414
4736
  claim. Stim signals only a claim owner whose recorded identity it can
@@ -4766,14 +5088,6 @@ not on any remote" (worktree remove)
4766
5088
  Expo build-cache provider would each use a different store. Set the named
4767
5089
  variable to an absolute path, or unset it to use the default. Metro and the
4768
5090
  cache provider, which cannot refuse, ignore a relative value with a warning.`
4769
- },
4770
- STIM_WORKER_FAILED: {
4771
- summary: "a programmatic API worker exited without returning an operation result",
4772
- body: () => `STIM_WORKER_FAILED (programmatic API)
4773
- The worker process ended without a structured result. StimError.details
4774
- includes its final stderr output. Inspect that output and call diagnostics()
4775
- for existing workspace logs, then stop() with a fresh signal to clean up
4776
- any resources created before the failure. See stim guide api.`
4777
5091
  },
4778
5092
  STIM_RUN_FAILED: {
4779
5093
  summary: "a programmatic operation failed without a more specific Stim code",
@@ -5029,9 +5343,10 @@ STOP DURING A BUILD
5029
5343
  lock waiting for \`stim ios\` (pid 41233, running for 12m04s) in this workspace to finish
5030
5344
  \`stop\` does not wait out a build it leaves with nothing to deploy to: a
5031
5345
  plain \`stop\`, \`stop --slot <name>\` for the slot the build targets, or
5032
- for the workspace's only device. It sends that run SIGINT, only after
5033
- proving the run's recorded process identity, and waits up to 60 seconds for
5034
- it to exit. The run stops its build tool, stores nothing from the
5346
+ for the workspace's only device. It asks that run to stop through the
5347
+ workspace state and, when the run is a CLI process, also sends it SIGINT,
5348
+ only after proving the run's recorded process identity. It waits up to 60
5349
+ seconds for the run to exit. The run stops its build tool, stores nothing from the
5035
5350
  interrupted build, and exits 130 with STIM_CANCELLED. A run that does not
5036
5351
  exit in time, or whose build tool outlived it, is refused with
5037
5352
  STIM_STOP_BLOCKED naming its pid and claim. \`stop --slot <name>\` while the
@@ -5061,11 +5376,20 @@ WAITING FOR A CHANGE
5061
5376
  changes, never two identical ones in a row. In a watch line, a usage
5062
5377
  figure repeats its value in the previous line until it moves by a step: an owner's cpuPercent 5 points, an
5063
5378
  owner's or environment's memoryMb and capacity.committedMb 16 MB. An owner's row
5064
- changes as a whole, so its residentMb and processes update with it. It reacts to Stim state files,
5065
- the EAS session ledger, adb device arrivals and departures, and simulator
5066
- state, and recomputes every 30 seconds as a fallback. A log append updates
5067
- only the log error count and device activity, no sooner than 15 seconds
5068
- after the previous refresh. Ctrl+C, SIGTERM or closing its stdout ends it
5379
+ changes as a whole, so its residentMb and processes update with it. A change
5380
+ in only a bookkeeping timestamp prints no line: each pullRequest.checkedAt,
5381
+ maintenance.lastChecks, maintenance.lastPass.startedAt and durationMs, and
5382
+ maintenance.running.startedAt. A line that prints carries their current
5383
+ values. It reacts to Stim state files,
5384
+ the EAS session ledger, git HEAD, index and ref changes in the worktrees'
5385
+ repositories, the exit of a recorded supervisor, Metro, build, browser or
5386
+ macOS app process (checked every 2 seconds), adb device arrivals and
5387
+ departures, and simulator boots, shutdowns, creations and deletions (from
5388
+ CoreSimulator's device set, with a simctl check every 30 seconds), and
5389
+ recomputes every 30 seconds as a fallback. A log append updates the log-derived facts
5390
+ within about a second; device activity follows at most every 5 seconds. The
5391
+ first line does not wait for git: worktree git summaries can be null in it,
5392
+ and the next line carries them. Ctrl+C, SIGTERM or closing its stdout ends it
5069
5393
  with exit 0. On Linux, so does the exit of the process that started it
5070
5394
  when its stdout is a pipe. It notices within a few seconds even when
5071
5395
  nothing changes, except a closed stdout pipe on Linux whose starter is
@@ -5114,7 +5438,7 @@ C-family header graphs, Swift package graphs and unresolved external input
5114
5438
  paths or compiler overrides. The miss reason explains the exclusion. --plan
5115
5439
  refuses such a prediction; omit --plan to prepare and build the app.
5116
5440
 
5117
- A compatible approved build Mac can compile a native Xcode app with
5441
+ A compatible approved remote Mac can compile a native Xcode app with
5118
5442
  --remote-build auto or --remote-build <name>. Both peers must support
5119
5443
  native-xcode-build. This first worker path requires a verified artifact identity
5120
5444
  and all inputs contained in the repository and visible to git. Ignored or
@@ -5161,8 +5485,34 @@ density/split APK sets and EAS/proxy targets are unsupported. Set
5161
5485
  org.gradle.configureondemand=false; configuration cache remains supported.
5162
5486
  Stim verifies the existing APK signature; it does not sign it.
5163
5487
 
5164
- Stim artifact caching is unavailable because arbitrary Gradle inputs are not fully
5165
- tracked. Build workers require a project-scoped \`android.offloadInputs\` declaration:
5488
+ Local APK caching requires project-scoped \`android.artifactInputs\`:
5489
+ \`{"complete":true,"ignored":[],"outputs":["build","app/build"],"localFiles":["../signing/release.keystore"],"environment":["SIGNING_PASSWORD"]}\`.
5490
+ complete promises a repeatable build from Git-visible source, declared ignored
5491
+ files, exact local files and named environment values, with pinned dependencies
5492
+ and unmodified pinned toolchains. Declare custom signing, JVM user.home, alternate
5493
+ toolchain and external configuration inputs; arbitrary Gradle reads cannot be
5494
+ inferred. Local files may be absolute or project-relative; absence is hashed.
5495
+ Default debug keystores, JDK release, SDK version metadata, Gradle user properties
5496
+ and init scripts also contribute. SDK directory paths alone do not change the
5497
+ key; other local.properties contents do. Keys must be plain and unescaped, and
5498
+ values must fit on one line; Java Properties escapes in sdk.dir values are supported.
5499
+ Unsupported syntax disables artifact caching. The AGP SDK must match ANDROID_HOME.
5500
+ Inputs are checked again after compilation; a change prevents publication.
5501
+ Cached APK bytes, package, signing certificate and target ABI are verified.
5502
+ A configured provider with a files capability shares the completed APK and
5503
+ verification receipt. Stim checks the local cache first, verifies downloaded
5504
+ bytes, then fills the local cache. Invalid shared entries are cache misses;
5505
+ providers without files are skipped. Private input contents are neither logged
5506
+ nor transferred. This contract grants no build-worker transfer access.
5507
+ Without it, native builds remain uncached and Gradle retains its incremental and
5508
+ task caches. optimizations.buildCache=false disables artifact reads and writes.
5509
+ --no-build-cache skips reads and shared-build waits, but stores the fresh build.
5510
+ Native Android --plan verifies the same key and cached APK without running Gradle;
5511
+ unresolved ABI (including legacy unowned emulator assignments), missing declarations,
5512
+ hosted placement and CAS setup refuse it.
5513
+
5514
+
5515
+ Build workers require a project-scoped \`android.offloadInputs\` declaration:
5166
5516
  \`{"complete":true,"ignored":["app/src/main/assets/generated.json"],"outputs":["build","app/build"]}\`.
5167
5517
  Use \`stim android --remote-build <approved-mac>\` after reviewing this declaration.
5168
5518
  Paths are exact repository-relative paths. complete affirms that Git-visible
@@ -5183,7 +5533,7 @@ Included builds (includeBuild, such as a build-logic plugin build) and
5183
5533
  externalNativeBuild staging (.cxx) are not reported by AGP and cannot be
5184
5534
  declared, so a remote build that creates them fails and Stim builds locally.
5185
5535
 
5186
- Native Android \`--plan\` refuses without executing Gradle; \`doctor\` reports native
5536
+ Native Android \`--plan\` requires a complete artifact input declaration and does not execute Gradle; \`doctor\` reports native
5187
5537
  prerequisites. \`reload\` refuses because the app has no Metro runtime. Re-run
5188
5538
  \`stim android\` after an edit and use \`stim stop\` for scoped cleanup.`
5189
5539
  },
@@ -5245,7 +5595,7 @@ A person on the host approves device-host access, separately from build, read
5245
5595
  and control. The setup in hosted-ios also serves Android. android.remote sets
5246
5596
  a workspace default. eas and proxy remain backends. --remote auto or
5247
5597
  android.remote = auto places automatically; see hosted-ios for the shared
5248
- policy. No flag or setting runs on this Mac.
5598
+ policy. No flag or setting runs on this machine.
5249
5599
 
5250
5600
  A named Mac is strict: refusal, unreachable, declined or failed preparation
5251
5601
  returns STIM_HOSTING_REFUSED without fallback. --device, a different recorded
@@ -5266,7 +5616,7 @@ bridge; the host reverses the client's Metro port into that bridge on its exact
5266
5616
  ledger-owned serial and sets debug_http_host to localhost:<clientMetroPort>.
5267
5617
  Each run and stim reload android restores the reverse, including after a host
5268
5618
  adb server restart. Reload broadcasts through the local Metro and never runs
5269
- adb against the host serial on this Mac. Release variants skip Metro. Launch
5619
+ adb against the host serial on this machine. Release variants skip Metro. Launch
5270
5620
  is true only with client bundle evidence or a live host release process;
5271
5621
  bundling requires a bundle request and unverified has no launch evidence.
5272
5622
 
@@ -5296,7 +5646,7 @@ arrive through local Metro.
5296
5646
 
5297
5647
  AGENT CONTROL
5298
5648
 
5299
- Set hosting.agentDriver to agent-device on the hosting Mac. Both Macs need
5649
+ Set hosting.agentDriver to agent-device on the hosting Mac. Both machines need
5300
5650
  agent-device 0.21.22 or later. The host advertises hosted-android-agent and
5301
5651
  starts one loopback daemon per installed Android session. Its policy allows
5302
5652
  only the exact ledger-owned emulator serial and denies device shutdown.
@@ -5337,8 +5687,8 @@ on the owned emulator until Stim deletes it. A daemon exit rotates only that
5337
5687
  session's grant; rerun stim android to refresh its config.
5338
5688
 
5339
5689
  Stim Desktop and the phone app view and control the emulator through this
5340
- Mac's local stim-server relay, with an "on <machine>" label. Turn on Serve to
5341
- phones in Desktop and pair the phone with this Mac, not the hosting Mac.
5690
+ machine's local stim-server relay, with an "on <machine>" label. Turn on Serve to
5691
+ phones in Desktop and pair the phone with this machine, not the hosting Mac.
5342
5692
  frames.subscribe and control.begin address workspace, platform android and
5343
5693
  slot, including named slots. The host verifies its exact ledger-owned serial,
5344
5694
  AVD identity and ABI before capture or input; clients never use that serial
@@ -5352,9 +5702,9 @@ are unavailable through the scrcpy path.
5352
5702
 
5353
5703
  Reruns send only missing manifest and APK digests from the session-scoped blob
5354
5704
  store. The host verifies reused bytes; each new attempt still installs the APK.
5355
- When an Android build ran on the hosting Mac's pinned node, the host takes only
5705
+ When an Android build ran on the hosting Mac's own stim-server, the host takes only
5356
5706
  the single App.apk matching the client's manifest. The build and hosting approvals
5357
- must belong to the same tailnet node. Handoff failure falls back to upload,
5707
+ must belong to the same client key. Handoff failure falls back to upload,
5358
5708
  retrying a busy host for at most one minute.
5359
5709
 
5360
5710
  stim logs, including --errors and --json, pulls native logcat records as src: device.
@@ -5385,8 +5735,7 @@ If Stim is not installed globally, replace stim with npx stim.
5385
5735
 
5386
5736
  stim settings set remote.machines '["janics-mac-mini"]'
5387
5737
  stim doctor --fix
5388
- # A person there approves: stim-server devices grant <id> --device-host
5389
- # Or runs stim-server setup on the worker with its client node, ticket and expiry.
5738
+ # A person there runs the stim-server setup command doctor printed.
5390
5739
  stim doctor
5391
5740
  stim ios --remote janics-mac-mini --device-type "iPhone 17 Pro" --runtime "iOS 27.0"
5392
5741
  stim status --json
@@ -5394,7 +5743,7 @@ If Stim is not installed globally, replace stim with npx stim.
5394
5743
  stim stop
5395
5744
 
5396
5745
  The same setup serves macos --remote. Approval is separate from build, read
5397
- and phone control. Set ios.remote to the machine name for a default (machine scope for the whole Mac);
5746
+ and phone control. Set ios.remote to the machine name for a default (machine scope for every app on this machine);
5398
5747
  no flag or setting runs here. eas and proxy keep their remote backend meanings.
5399
5748
  --remote auto (or ios.remote = auto / android.remote = auto) stays here when
5400
5749
  there is a free concurrency.maxDevices slot (0 means unlimited), no device
@@ -5406,7 +5755,8 @@ members from new automatic placement only; an excluded local member is never a
5406
5755
  fallback. Existing sessions keep their owner and named placement bypasses the pool.
5407
5756
  See stim guide settings for separate build/device membership controls.
5408
5757
 
5409
- Otherwise Stim asks every approved remote.machines host in parallel, with a
5758
+ Otherwise Stim asks every approved remote.machines host in parallel, skipping
5759
+ one that tailscale status reports as running an OS other than macOS, with a
5410
5760
  3-second probe timeout and this run's model/runtime or image/profile selectors.
5411
5761
  A compatible choice, no declined reason, normal memory pressure and available
5412
5762
  capacity are required. With a free local slot, a host must report a lower load;
@@ -5415,11 +5765,11 @@ Hosts rank by the build preference (flag > STIM_REMOTE_BUILD > remote.build)
5415
5765
  when it names a machine, then lowest load, most free memory and remote.machines order. Automatic build
5416
5766
  offload resolves later, after the device architecture is known.
5417
5767
 
5418
- When no host admits and this Mac is at its concurrency.maxDevices cap (or runs
5768
+ When no host admits and this machine is at its concurrency.maxDevices cap (or runs
5419
5769
  are queued ahead), remote.easFallback (default false; EAS Simulator is billed)
5420
5770
  runs the device on an EAS Simulator exactly as --remote eas would: the build
5421
5771
  stays here, nothing starts an EAS cloud build, stop ends the session, and
5422
- status reports it under remoteDevices. A busy Mac with a free slot never uses
5772
+ status reports it under remoteDevices. A busy machine with a free slot never uses
5423
5773
  it. Before choosing it Stim checks, without starting a session, that eas-cli
5424
5774
  has simulator commands (and 22.2.0+ for --device-type), eas
5425
5775
  simulator:availability says this project's account can use it, agent-device
@@ -5427,7 +5777,7 @@ is on PATH, the slot is default, no --runtime, --system-image or
5427
5777
  --device-profile flag is given, no EAS Simulator session of this workspace runs
5428
5778
  another platform or model, and a Debug run's Metro is reachable (not
5429
5779
  metro.tunnel off; on an Expo tunnel, run stim start --remote first). A recorded
5430
- EAS session is not sticky: once this Mac has room, auto runs here and the
5780
+ EAS session is not sticky: once this machine has room, auto runs here and the
5431
5781
  session bills until stim stop. If any check fails, or the
5432
5782
  setting is off, auto runs here and may wait in the existing FIFO device
5433
5783
  slot queue; --no-wait and --wait 0 refuse with STIM_AT_CAPACITY. The placement
@@ -5452,7 +5802,7 @@ here or elsewhere. --device cannot use a hosting Mac, and --simulator-app
5452
5802
  refuses. The host boots headless and ignores this Mac's iosSimulatorApp setting.
5453
5803
  --slot, --device-type and --runtime select the hosted simulator; --scheme,
5454
5804
  --configuration and --eas-profile select the app as usual. The build targets
5455
- the offered simulator architecture, not this Mac's. --remote-build remains
5805
+ the offered simulator architecture, not this machine's. --remote-build remains
5456
5806
  independent and selects a compatible build worker for a Debug cache miss.
5457
5807
 
5458
5808
  The host's own owned devices count toward concurrency.maxDevices.
@@ -5475,7 +5825,7 @@ sessions.
5475
5825
 
5476
5826
  Stim builds or fetches before reserving, records the session immediately, then
5477
5827
  uploads the app on every run. React Native and Expo Debug keep Metro here; the supervisor owns a
5478
- private gateway bound to this Mac's Tailscale address, pinned to the host peer
5828
+ private gateway bound to this machine's Tailscale address, pinned to the host peer
5479
5829
  and protected by a per-session secret. The host exposes only a loopback bridge.
5480
5830
  No start --remote is needed; metro.tunnel and metro.publicUrl are ignored.
5481
5831
  Debug requires a running supervisor with private gateway support before reserving
@@ -5487,7 +5837,7 @@ apps use process mode in every configuration and require no Metro; see native-io
5487
5837
 
5488
5838
  A rerun on the same named Mac reattaches and delivers a new app attempt. A
5489
5839
  stopped or missing session is replaced; an unreachable or unknown owner refuses
5490
- replacement. Switching between this Mac and a hosting Mac, or to a different
5840
+ replacement. Switching between this machine and a hosting Mac, or to a different
5491
5841
  hosting Mac, refuses for that slot: run stim stop first.
5492
5842
  Status adds ios.host { machine, session, selected, device: { name, runtime },
5493
5843
  agent, state } per slot. A shutdown local simulator is replaced in status; a
@@ -5555,7 +5905,8 @@ tail before parking or deletion, including on revocation or server close; stop c
5555
5905
  back afterwards. If the final collection drops a backlog interval and eventually
5556
5906
  succeeds, a device warning record names that interval. A damaged collection checkpoint is ignored and
5557
5907
  rebuilt; it never blocks reading collected records. The client waits up to
5558
- 180 seconds for stop. The final collection and worker termination paths fit within
5908
+ 280 seconds for stop. The final collection, parking, the stop that follows a failed
5909
+ park and worker termination paths fit within
5559
5910
  that wait; an in-flight handoff copy and closing Metro or view transports are
5560
5911
  outside those worker bounds.
5561
5912
  Collected records remain in the session home after parking or deletion;
@@ -5567,7 +5918,7 @@ host gets an update note, uses uploads and shows logs already copied here.
5567
5918
 
5568
5919
  AGENT CONTROL
5569
5920
 
5570
- On the hosting Mac, set hosting.agentDriver to agent-device. Both Macs need
5921
+ On the hosting Mac, set hosting.agentDriver to agent-device. Both machines need
5571
5922
  agent-device 0.21.20 or later (the policy and remote-config contract). The host
5572
5923
  starts one loopback daemon per installed iOS session under a child-aware
5573
5924
  ownership claim. Its daemon policy pins exactly that simulator UDID and denies
@@ -5624,8 +5975,8 @@ daemon startup or grant issuance on client support. The client checks the host's
5624
5975
  hosted-ios-agent feature before using a grant.
5625
5976
 
5626
5977
  Stim Desktop and the phone app view and control the simulator through this
5627
- Mac's stim-server relay, with an "on <machine>" label. Turn on Serve to phones
5628
- in Desktop; pair the phone with this Mac, not the hosting Mac. frames.subscribe
5978
+ machine's stim-server relay, with an "on <machine>" label. Turn on Serve to phones
5979
+ in Desktop; pair the phone with this machine, not the hosting Mac. frames.subscribe
5629
5980
  and control.begin address workspace, platform ios and slot, including named
5630
5981
  slots. The relay shares one authenticated host connection with hosted macOS
5631
5982
  viewers. Stopped sessions show a rerun remedy; unavailable hosts report why
@@ -6195,8 +6546,8 @@ PREDICTING THE NEXT BUILD (--plan)
6195
6546
  architecture or ABI from that Mac's device offer, the question a run asks
6196
6547
  before it reserves anything. With auto it repeats the placement decision a
6197
6548
  run would make now and reports where in the payload's placement field, for
6198
- example "this Mac; auto may use janics-mac-mini". When the plan stays on
6199
- this Mac and a listed Mac would build for another architecture, placement
6549
+ example "this machine; auto may use janics-mac-mini". When the plan stays on
6550
+ this machine and a listed Mac would build for another architecture, placement
6200
6551
  says so, since that Mac's key differs. The plan only predicts the key: a
6201
6552
  run reads the architecture of the device it actually gets. A Mac that does
6202
6553
  not answer, ios.remote or android.remote set to eas or proxy, or auto that
@@ -6274,11 +6625,12 @@ A RUNNING APP IS RESTARTED, LIKE XCODE'S RUN
6274
6625
  A stop that fails refuses the launch rather than reusing the old process.
6275
6626
  When a simulator's process list cannot be read, Stim launches with
6276
6627
  \`simctl launch --terminate-running-process\`, which replaces any running
6277
- copy. When an emulator's cannot be read, Stim launches without stopping
6278
- anything and verification reports what it observes. An iPhone run
6279
- (\`ios --device\`) needs no extra step: its collector launches with
6280
- devicectl's \`--terminate-existing\`. Remote targets launch through
6281
- \`agent-device open --relaunch\`.
6628
+ copy. When an Android device's cannot be read, Stim launches with
6629
+ \`am start -S\`, which force-stops the app first; an app without a
6630
+ launcher activity gets a best-effort \`am force-stop\` before \`monkey\`.
6631
+ Neither claims a restart. An iPhone run (\`ios --device\`) needs no extra
6632
+ step: its collector launches with devicectl's \`--terminate-existing\`.
6633
+ Remote targets launch through \`agent-device open --relaunch\`.
6282
6634
 
6283
6635
  RUNTIME STATE AND BUILD INPUTS
6284
6636
  Runtime state is stored outside the project tree under
@@ -6391,7 +6743,8 @@ then compiles through the cache the selection leaves -- ccache, or none when
6391
6743
  compilerCache is none. It never refuses. See
6392
6744
  https://stim.appandflow.com/docs/android-cas for setup, evidence, and limits.
6393
6745
  Compiler/PCH modes have separate generated directories under each module's
6394
- .cxx/stim-<profile> (or custom staging root). Switching modes in Stim selects
6746
+ .cxx/stim-<profile> (.cxx/<first 4 profile characters> on Windows, or custom
6747
+ staging root). Switching modes in Stim selects
6395
6748
  the matching directory; direct Gradle builds keep their own configuration.
6396
6749
  Generated CAS directories still depend on Stim's environment and adapter paths.
6397
6750
 
@@ -6433,6 +6786,14 @@ THE BUILD CACHE HAS THREE LEVELS
6433
6786
  tier because its run-options contract cannot distinguish ABIs; levels one
6434
6787
  and two remain ABI-keyed and active.
6435
6788
 
6789
+ The "eas" tier needs eas-build-cache-provider in the app and an installed
6790
+ eas-cli in the app or on PATH. Stim runs its fixed EAS CLI commands without
6791
+ loading the provider SDK into the caller. Fingerprint generation uploads
6792
+ fingerprint metadata to EAS. Fingerprint and transfer commands share one deadline per phase; timeout or
6793
+ cancellation stops that phase's CLI process before returning.
6794
+ Stim never installs eas-cli during cache lookup. A custom Windows batch
6795
+ launcher needs a project eas-cli installation so Stim can stop Node directly.
6796
+
6436
6797
  Stim never configures a provider and never suggests changing one: a
6437
6798
  project without one is a perfectly ordinary local-only project (doctor does
6438
6799
  not ask for one either -- a provider only serves builds run OUTSIDE Stim).
@@ -6956,9 +7317,10 @@ OPT-IN CONCURRENCY LIMITS (UNLIMITED BY DEFAULT)
6956
7317
  paths eligible under the source checkout's Git ignore rules, including .env
6957
7318
  and local configuration. The source's nonempty
6958
7319
  .worktreeexclude replaces its resolved worktree.exclude setting. Nested
6959
- registered worktrees, .DS_Store, .DerivedData, .idea, .jj, and
6960
- android/build/generated/autolinking caches are excluded, including inside
6961
- newly copied directories. Gradle regenerates autolinking
7320
+ registered worktrees, .DS_Store, .DerivedData, .idea, .jj, a project-local
7321
+ .pnpm-store, and android/build/generated/autolinking caches are excluded,
7322
+ including inside newly copied directories; pnpm refills its store from the
7323
+ network or its global store. Gradle regenerates autolinking
6962
7324
  for the destination checkout on its next build. Warm also skips paths
6963
7325
  overlapping a nested destination worktree or below a symlink ancestor.
6964
7326
  Tracked .idea settings come from Git and stay untouched by warm.
@@ -7175,6 +7537,33 @@ OPT-IN CONCURRENCY LIMITS (UNLIMITED BY DEFAULT)
7175
7537
  a CACHED app falls back to a full build; on a freshly built one it exits on
7176
7538
  its own code, because building again would produce the same app.
7177
7539
 
7540
+ PROVISIONING A NEW PHONE is a separate, explicit step, never part of a
7541
+ build: \`stim device provision ios [udid]\` finds the Xcode project the way
7542
+ \`ios\` does (ios/ for React Native and Expo, the app's own .xcodeproj or
7543
+ .xcworkspace for a native app), reads every target of the scheme that is
7544
+ signed with a profile, on any platform (\`--scheme\`, \`--configuration\`,
7545
+ or ios.scheme / ios.configuration), prints the team, the phone and each
7546
+ bundle id whose Development profile it would create or refresh, and asks
7547
+ [y/N]; Ctrl+C or end of input is no, and \`--yes\` replaces the prompt
7548
+ without a terminal. It then runs xcodebuild -allowProvisioningUpdates
7549
+ -allowProvisioningDeviceRegistration for that phone in a throwaway
7550
+ DerivedData and stops it at the first build task, which xcodebuild starts
7551
+ only once planning has resolved signing, before compilation. A native app
7552
+ with Swift packages uses the packages \`ios\` already resolved, with
7553
+ automatic package resolution off, and refuses until \`stim ios\` has
7554
+ resolved them. That stop and a Ctrl+C send xcodebuild
7555
+ SIGINT, then SIGTERM after 10s and SIGKILL 5s later; a SIGTERM sent to Stim,
7556
+ and the 15-minute timeout, send SIGTERM, then SIGKILL after 5s. A second
7557
+ signal kills xcodebuild before Stim exits. The DerivedData is always
7558
+ removed. It authenticates with the Xcode account, or with
7559
+ the App Store Connect API key in ios.appStoreConnectKeyPath,
7560
+ ios.appStoreConnectKeyId and ios.appStoreConnectIssuerId (or
7561
+ STIM_ASC_KEY_PATH, STIM_ASC_KEY_ID, STIM_ASC_ISSUER_ID, each overriding its
7562
+ own setting); it never reads the key and never prints its path. It reports
7563
+ the installed profile and any new keychain identity, and on a failure or
7564
+ interrupt it still reports what had already changed. It changes the team's
7565
+ developer account: agents run it only on the user's explicit go-ahead.
7566
+
7178
7567
  DEBUG REACHES METRO OVER THE LAN, because a phone shares no loopback with
7179
7568
  the host and USB carries no reverse forward. Stim picks a non-internal IPv4
7180
7569
  address (en0 first, RN's own order from react-native-xcode.sh), gates it as
@@ -7550,11 +7939,16 @@ scoped STIM_HOME homes unless STIM_MAINTENANCE is explicit, so a scratch home
7550
7939
  or CI run never deletes on its own.
7551
7940
  Commands trigger a detached pass when a check is due: disk and memory pressure
7552
7941
  (every minute), directory sizes (hourly), finished worktrees (every 15
7553
- minutes) and the age sweep (daily). guide, settings and help do not trigger
7554
- it; gc --delete also skips the hook. status --watch also triggers checks.
7942
+ minutes) and the age sweep (daily). guide, settings, help and every gc
7943
+ invocation do not trigger it; gc without --delete prints the maintenance plan
7944
+ itself.
7945
+ status --watch and every running dev server supervisor also trigger checks
7946
+ each minute, so the disk check runs while no command does.
7555
7947
  Size, worktree and sweep checks defer under high load. Attempts back off for at
7556
7948
  least one minute. Measured directories are workspace build outputs and Stim's
7557
- shared native, Metro, ccache, Swift compilation and registered caches.
7949
+ shared native, Metro, ccache, Swift compilation and registered caches. The size
7950
+ check also records the disk of each owned and parked simulator and emulator
7951
+ (category owned-devices); status prints the total as "owned devices".
7558
7952
  Pressure checks read free disk on the Stim home, projects and worker root
7559
7953
  volumes. On macOS the memory signal is the sysctl pressure level, with no
7560
7954
  signal if sysctl fails. Other platforms use os.freemem(); macOS never falls
@@ -7572,20 +7966,83 @@ In on mode a pass runs, cheapest to rebuild first:
7572
7966
  maintenance.capTargetPercent of caches.buildCacheMaxGb or
7573
7967
  caches.metroCacheMaxGb, and entries unused for olderThanDays
7574
7968
  4. the Swift compilation cache, emptied whole, only below
7575
- budget.hardFloorDiskGb and with no build lock or slot held
7969
+ budget.hardFloorDiskGb, and kept while an iOS build holds its shared
7970
+ claim (taken for each xcodebuild run that compiles with it) or that
7971
+ claim is unresolved; the emptying holds the exclusive claim
7576
7972
  5. linked worktrees whose branch or pull request finished (gc's finished
7577
7973
  worktree rules and gc.worktreeGraceMinutes; maintenance.removeFinishedWorktrees)
7974
+ 6. owned simulators and emulators of a finished worktree that is kept, such
7975
+ as a dirty one (not one locked with git worktree lock), once it finished
7976
+ and was idle for gc.worktreeGraceMinutes (at least protectRecentHours),
7977
+ and of any
7978
+ workspace idle for maintenance.deviceIdleDays (3; 0 turns it off) in the
7979
+ age sweep. Devices are recreated on the next run.
7578
7980
  Disk-driven steps stop once free space is back above the floor. Kept for
7579
7981
  every pass: a workspace that is in use, holds a lock, was used within
7580
7982
  maintenance.protectRecentHours, is pinned with maintenance.keep, or is the
7581
7983
  workspace of the command that started the pass; cache entries used within
7582
7984
  protectRecentHours, named by a project's last builds, a parked device or a
7583
7985
  live build lock, or in a Metro store with a running or unverifiable dev
7584
- server. Anything the pass cannot verify is kept. A pass never shuts down idle devices or dev servers or stops watchman; removing a
7986
+ server. Anything the pass cannot verify is kept. A device is deleted only when
7987
+ its workspace is idle: no dev server, native run, build, lease, managed lock,
7988
+ driven or busy device, and no recorded use within the window. Below disk
7989
+ pressure level 3 a pass never stops a dev server or watchman; removing a
7585
7990
  finished worktree or orphaned directory does tear down that workspace's own
7586
7991
  dev server, owned devices and Chrome profile, as gc --delete does. ccache evicts by itself under
7587
7992
  caches.ccacheMaxGb.
7588
7993
 
7994
+ DISK PRESSURE LEVELS
7995
+ Level 1 is free disk under the floor above (budget.minFreeDiskGb, 20 GB).
7996
+ Level 2 is under maintenance.urgentFreeDiskGb (12), and level 3 is under
7997
+ maintenance.criticalFreeDiskGb (6) or budget.hardFloorDiskGb, where builds
7998
+ refuse. When level 1 leaves free disk under the floor, a pass goes on with
7999
+ level 2, which runs until free disk is back above the floor; level 3 runs
8000
+ only while free disk stays under the level 3 floor. At levels 2 and 3 the
8001
+ protection window is maintenance.pressureProtectMinutes (10) instead of
8002
+ protectRecentHours. Each step re-measures first and stops once free disk is
8003
+ back above its level's floor, cheapest to rebuild first.
8004
+ Level 2:
8005
+ 1. build outputs of workspaces unused for the short window
8006
+ 2. parked simulators and emulators, deleted through centralized teardown
8007
+ 3. build directories of idle worktrees: ios/build, android/app/build,
8008
+ android/build, android/.gradle and .build, at a project root, the
8009
+ worktree root, or apps/* and packages/*
8010
+ 4. owned simulators and emulators of workspaces idle for the short window
8011
+ 5. Stim temp directories and xctrace recordings in TMPDIR unmodified for
8012
+ 24 hours, with gc --delete's checks (see stim guide cleanup gc)
8013
+ Level 3:
8014
+ 1. the Swift compilation cache, emptied whole under the same build rule;
8015
+ when a build keeps it, the last pass's blocked reasons name its size
8016
+ and the build
8017
+ 2. finished worktrees, and clean, pushed worktrees with no commits of
8018
+ their own beyond the default branch, with the short window as grace
8019
+ period (maintenance.removeFinishedWorktrees still applies)
8020
+ 3. workspaces whose dev server runs but that are otherwise inactive for
8021
+ the short window: their dev server stops and their owned simulators
8022
+ and emulators are deleted
8023
+ 4. node_modules and ios/Pods at the root or a project root of idle linked
8024
+ worktrees, never the source checkout
8025
+ 5. idle Gradle and Kotlin daemons, stopped as gc --delete --cache
8026
+ gradle-daemons stops them (never during a Stim build, never a busy or
8027
+ unverified one, never with STIM_HOME set), only when every Gradle
8028
+ process that keeps step 6 from running is such a daemon; the pass
8029
+ waits up to 3 seconds for them to exit
8030
+ 6. Gradle version caches and wrapper distributions no registered project
8031
+ pins, once unmodified for maintenance.gradleVersionIdleDays days (7;
8032
+ 0 turns steps 5 and 6 off), with the same checks as gc --cache
8033
+ gradle-versions, run again after step 5 so a daemon that did not exit
8034
+ still keeps them
8035
+ A directory is cleared only when it is a real directory inside its
8036
+ worktree, git tracks nothing in it, its worktree is not in use, holds no
8037
+ lock or build and is not pinned, and nothing under it, nor the worktree's
8038
+ git index, HEAD or Stim state, changed within the short window. A dirty or
8039
+ unpushed worktree can still be stopped and have these directories cleared;
8040
+ only its removal is refused. Caches of other tools (pnpm and npm stores,
8041
+ CocoaPods, SwiftPM, the shared Gradle caches) are never deleted.
8042
+ status shows the level and, at level 3, the shortfall in the last pass;
8043
+ every command prints one stderr line while the last check, at most 10
8044
+ minutes old, measured level 3.
8045
+
7589
8046
  stim status last checks, plan and running pass
7590
8047
  stim status --json maintenance observations and recent records
7591
8048
  stim logs --source maintenance this workspace's maintenance records
@@ -7600,6 +8057,9 @@ In report mode actions and explaining skips are logged only when newly
7600
8057
  planned; in on mode every action taken, failure and explaining skip is logged
7601
8058
  with its bytes, and a pass record carries the bytes freed. Removal of a
7602
8059
  worktree or orphaned directory is logged to the machine log only.
8060
+ An iOS build that fails on a corrupt compilation cache logs the emptying,
8061
+ or why Stim left the cache alone, with trigger "ios build", in every mode;
8062
+ see \`stim guide errors STIM_COMPILATION_CACHE_CORRUPT\`.
7603
8063
  maintenance.logChecks enables debug observations; default false.
7604
8064
  status and doctor report invalid settings and unresolved claims with a removal
7605
8065
  command to run only after confirming the holder is gone. Invalid cache caps
@@ -7607,6 +8067,10 @@ fall back to their own defaults; invalid maintenance settings disable passes.
7607
8067
  Pin a workspace with \`stim settings set maintenance.keep true\` in its project.
7608
8068
  The run claim serializes passes with gc --delete; gc refuses a held claim
7609
8069
  with the holder and recovery guidance instead of waiting.
8070
+ Each pass keeps a 1 MiB file, maintenance/disk-reserve. When taking the run
8071
+ claim fails with ENOSPC or EDQUOT, the pass deletes it and retries once, so a
8072
+ pass starts at zero free disk as long as an earlier pass left the reserve; the
8073
+ pass writes it again at its end.
7610
8074
 
7611
8075
  CLEANUP AND DISK
7612
8076
 
@@ -7622,15 +8086,22 @@ See stim guide logs for AGENT_DEVICE_STATE_DIR examples.
7622
8086
  WHAT RECLAIMS AN OWNED DEVICE
7623
8087
  stim worktree remove parks eligible owned simulators and emulators
7624
8088
  (\`guide lifecycle pool\`); deletes them when
7625
- parking is disabled or their setup cannot be verified
8089
+ parking is disabled, their setup cannot be
8090
+ verified, or free disk is at pressure level 2 or 3
7626
8091
  stim gc --delete sweeps devices this Stim home created that no project
7627
8092
  references (\`guide cleanup gc\`), clears
7628
8093
  verified parked simulators and emulators, and
7629
- runs \`stim worktree remove\` on every clean,
7630
- Stim-managed linked worktree whose branch is merged,
8094
+ runs \`stim worktree remove\` on every clean
8095
+ linked worktree of a registered repository whose
8096
+ branch is merged,
7631
8097
  or whose pull request was merged or closed, and
7632
8098
  that shows no activity within
7633
- gc.worktreeGraceMinutes
8099
+ gc.worktreeGraceMinutes, and deletes the owned
8100
+ devices of a finished worktree it keeps (dirty,
8101
+ unpushed, locked) when its workspace is idle for
8102
+ that grace and not pinned by maintenance.keep
8103
+ automatic maintenance deletes the owned devices of idle workspaces
8104
+ (MAINTENANCE above)
7634
8105
  stim gc --delete --older-than <days>
7635
8106
  also reaps the device of a workspace no Stim
7636
8107
  command has used in that long, even though the
@@ -7708,7 +8179,7 @@ to stats.json.corrupt-<unix ms> and starts a new one.`,
7708
8179
  Archived log subscriptions send retained records, then logs-ended.
7709
8180
  Archived frame subscriptions need at and cannot be physical or go live.
7710
8181
  Replay ranges report recording disabled.
7711
- Reads stay on this Mac.
8182
+ Reads stay on this machine.
7712
8183
  An updated server is required; older servers refuse with bad-request when
7713
8184
  workspace is omitted. There is no CLI archive log flag.
7714
8185
  archive.enabled defaults true and accepts every settings layer. STIM_HOME
@@ -7781,6 +8252,69 @@ caches, never changes actionable, and --delete and --older-than never select
7781
8252
  it. Any --cache scope, including all, omits measurement and reports
7782
8253
  sections.swiftpmCache: null. Only stats and unscoped gc measure it.
7783
8254
 
8255
+ GRADLE VERSIONS
8256
+ \`stim gc --cache gradle-versions\` lists, and with --delete removes, the
8257
+ caches/<version> and wrapper/dists/gradle-<version>-* directories under
8258
+ GRADLE_USER_HOME of each Gradle version no registered project pins, read from
8259
+ the wrapper of each project's Android Gradle root and root. Nothing is deleted
8260
+ while a process that may be Gradle using this home runs: a Gradle main
8261
+ class, a Gradle wrapper or distribution jar, or a java process naming a path
8262
+ under the home, unless it names no such path and every gradle.user.home or
8263
+ -g it names is an existing Gradle home elsewhere, while ps fails, while no Stim config
8264
+ exists, while a registered project is missing or its pin cannot be read,
8265
+ while a Gradle directory cannot be listed, or while caches, wrapper or
8266
+ wrapper/dists is a symlink or resolves outside the home;
8267
+ sections.gradleVersions.blocked names the reason. A version is also kept
8268
+ when one of its paths is not a plain directory inside the home, when an
8269
+ entry under it cannot be listed or stat'd, when it holds more than 500000
8270
+ entries, or, with --older-than <days>, when anything under it changed within
8271
+ that many days.
8272
+ Right before removal gc lists processes and reads pins again, then renames
8273
+ each directory to a .stim-delete-* sibling and deletes it. A later gc
8274
+ --delete --cache gradle-versions finishes any .stim-delete-* left by a failed
8275
+ removal, even while the versions are blocked (not while the home is outside
8276
+ a scoped STIM_HOME or its layout is unsafe), since nothing uses those
8277
+ directories, and reports it again when that fails; plain gc --delete and
8278
+ automatic maintenance never touch them. modules-2, build-cache-1, jdks and
8279
+ other shared Gradle caches are never deleted, and --cache all never includes these versions.
8280
+ With STIM_HOME set, gc deletes nothing in a Gradle home outside it. At disk
8281
+ pressure level 3 automatic maintenance first stops the idle Gradle and Kotlin
8282
+ daemons gc --delete --cache gradle-daemons would stop, only when every Gradle
8283
+ process that keeps a version is such a daemon, then does the same for
8284
+ versions unmodified for maintenance.gradleVersionIdleDays days (7; 0 turns
8285
+ both off). A manual gc --delete --cache gradle-versions never stops daemons.
8286
+
8287
+ OTHER TOOLS' MACHINE-WIDE CACHES (REPORT ONLY)
8288
+ Unscoped gc also reports large caches other tools keep for the whole machine,
8289
+ each with its size and the owning tool's own remedy:
8290
+ - Gradle version caches and wrapper dists under GRADLE_USER_HOME (default
8291
+ ~/.gradle) for versions no registered project pins in the
8292
+ gradle/wrapper/gradle-wrapper.properties of its Android Gradle root
8293
+ (android.gradleRoot, default android, relative to the app, inside the
8294
+ repository; a custom root must hold settings.gradle and the module's build
8295
+ script) or of the project root, and caches/modules-2. A registered project
8296
+ that is missing, whose settings cannot be resolved, or whose wrapper cannot
8297
+ be read or names no Gradle version is named, since it may still pin a
8298
+ listed version; every version a readable wrapper of it pins still counts as
8299
+ pinned. A directory that cannot be listed adds a notice and marks the
8300
+ section incomplete.
8301
+ Gradle removes version caches and modules-2 entries unused for 30 days on
8302
+ its own. The remedy for a version is stim gc --delete --cache
8303
+ gradle-versions (see GRADLE VERSIONS); modules-2 stays report only.
8304
+ - iOS simulator runtimes with their simulator count. Only a deletable
8305
+ runtime no simulator uses gets the remedy xcrun simctl runtime delete <id>.
8306
+ - Xcode device support entries under ~/Library/Developer/Xcode/*DeviceSupport.
8307
+ Xcode copies an entry again when that device next connects.
8308
+ - agent-device runner builds grouped by agent-device and Xcode build version,
8309
+ compared with the installed agent-device and Xcode. agent-device has no
8310
+ prune command; remove an entry not in use by hand.
8311
+ $STIM_HOME/machine-cache-usage.json caches the sizes, simulator runtimes and
8312
+ tool versions for 10 minutes and measures again when the measured
8313
+ directories change. Stim never deletes any of these. They are never in
8314
+ caches, never change actionable, and --delete and --older-than never select
8315
+ them. Any --cache scope omits measurement and reports sections.machineCaches:
8316
+ null.
8317
+
7784
8318
  LINKED WORKTREES
7785
8319
  \`stim worktree remove\` works with any linked worktree, warmed or not.
7786
8320
  Git registration identifies the worktree; a Stim registry entry is not
@@ -7811,20 +8345,43 @@ IN USE
7811
8345
  existence cannot be read (a permission error) is never treated as deleted.
7812
8346
 
7813
8347
  SWEEPING FINISHED WORKTREES
7814
- Every \`gc\` without --cache looks at every registered project root and
7815
- every workspace.json root, grouped by git worktree, and reports each linked
8348
+ Every \`gc\` without --cache looks at every registered project root,
8349
+ every workspace.json root, and every linked worktree \`git worktree list\`
8350
+ shows for a registered project's repository, whether or not a stim command
8351
+ ever ran in it. It groups them by git worktree and reports each linked
7816
8352
  worktree with the reason it is removed or kept. A worktree is finished when
7817
8353
  its branch is merged into the default branch, or its pull request was
7818
8354
  merged or closed (PULL REQUESTS below). \`--worktrees\` also removes
7819
8355
  one that is idle: no recorded use for --older-than days, 7 without it; a
7820
- worktree whose last use is unknown is kept. Both need the same clean state;
7821
- a worktree is kept when it is the source checkout, bare, locked, in use,
8356
+ worktree whose last use is unknown is kept, unless it has no commits of its
8357
+ own beyond the default branch (NO COMMITS OF ITS OWN below). Both need the
8358
+ same clean state;
8359
+ a worktree is kept when it is the source checkout, bare, locked, holds
8360
+ another worktree (worktree remove refuses it too), in use,
7822
8361
  dirty (untracked files count; pod install churn and
7823
8362
  watchman's untracked \`.watchman-cookie-*\` files alone do not), unpushed
7824
8363
  (commits no remote-tracking ref or other local branch reaches), or has
7825
8364
  initialized submodules. Without --worktrees, the report leaves out the
7826
8365
  source checkout and roots outside git.
7827
8366
 
8367
+ Each worktree line also shows its in-tree build state: the size of the
8368
+ build and dependency directories maintenance clears (DISK PRESSURE LEVELS
8369
+ above) and a root .pnpm-store, largest first (APFS clones count in full).
8370
+ A size comes from the disk-usage cache while it is under an hour old and
8371
+ no entry was added to or removed from its directory since (a build that
8372
+ only grows files deeper inside shows within the hour); otherwise gc runs
8373
+ one du per directory, 4 at once, 10 seconds each, and a directory du did
8374
+ not finish shows "size unknown". du counts hard links shared between two
8375
+ directories in both. gc never deletes them on their own;
8376
+ the JSON field is buildState. Only directories of this worktree count,
8377
+ not those of a worktree nested inside it.
8378
+
8379
+ A directory directly under a registered repository's .worktrees/ that
8380
+ \`git worktree list\` does not show and that no git knows (no .git, or a
8381
+ .git file whose gitdir is gone, as after a prune) is listed under "Directories under .worktrees that git does
8382
+ not list as worktrees" with its size and the rm -rf command, and never
8383
+ removed: gc cannot check it for uncommitted work. Look through it first.
8384
+
7828
8385
  IN USE means its dev server supervisor runs, a stim ios, android or stop
7829
8386
  run holds its native-run lock, a live build lock or slot names it, a
7830
8387
  managed tunnel or remote lock is held, an owned simulator or emulator of
@@ -7846,9 +8403,32 @@ SWEEPING FINISHED WORKTREES
7846
8403
  activity cannot be read, the worktree is kept (reason activity-unknown).
7847
8404
  0 turns the grace period off.
7848
8405
 
8406
+ FRESH STATE: \`gc --delete\` and the maintenance worktree check fetch the
8407
+ default branch and ask gh as MERGED and PULL REQUESTS below describe. The
8408
+ worktree check runs in the background when a stim command starts a
8409
+ maintenance pass that has it due, every maintenance.worktreeCheckMinutes;
8410
+ plain gc never starts one. It writes each pull request lookup into the
8411
+ cache \`status --watch\` also writes. A report without --delete runs no
8412
+ fetch and no gh: it judges merges from the existing origin/<default> ref and
8413
+ takes a pull request from that cache when it records the worktree's current
8414
+ branch and HEAD. Both count as fresh while they are younger than twice the
8415
+ worktree check interval, and never less than 10 minutes. A merge found
8416
+ against the existing ref stays true as the default branch moves on, so a
8417
+ report calls a worktree removable only on facts fresh state keeps, with
8418
+ three exceptions: a force-push that dropped the merge from the default
8419
+ branch, a pull request reopened since the lookup, and the grace period of a
8420
+ pull request merged or closed after its merge commit was made. A kept worktree whose
8421
+ verdict fresher state could change says "merge state not refreshed", and
8422
+ its reason not-merged becomes merge-unknown. With maintenance.mode off or
8423
+ maintenance.removeFinishedWorktrees false the check never runs, and only
8424
+ \`gc --delete\` refreshes. \`gc --delete\` always collects fresh state
8425
+ before it removes anything; \`stim worktree remove\` run from a report
8426
+ checks only for uncommitted and unpushed work.
8427
+
7849
8428
  MERGED means, with the default branch taken from origin/HEAD, after a
7850
8429
  \`git fetch origin <default>\` per repository (30s timeout, no credential
7851
- prompt; skipped when that checkout fetched in the last 10 minutes):
8430
+ prompt; skipped when that checkout fetched in the last 10 minutes; FRESH
8431
+ STATE above for a report):
7852
8432
  - HEAD is reachable from origin/<default> through a merge commit, and the
7853
8433
  branch's reflog shows a commit made on it that HEAD contains. A branch
7854
8434
  with no commit of its own -- fresh, cut from another branch, or reset
@@ -7869,8 +8449,23 @@ SWEEPING FINISHED WORKTREES
7869
8449
  not block removal, because their change is on the default branch, and the
7870
8450
  branch is kept.
7871
8451
 
7872
- PULL REQUESTS: gc runs one \`gh api graphql\` query per repository that
7873
- asks for the 20 newest pull requests of each linked worktree's branch, as
8452
+ NO COMMITS OF ITS OWN: a worktree whose HEAD the default branch contains
8453
+ and whose branch has no commit made on it (fresh, cut from another
8454
+ branch, or reset onto one; the MERGED rule above) holds nothing git would
8455
+ lose, but looks the same as one just made by git worktree add. Plain gc keeps
8456
+ it (reason not-merged, "no commits of its own beyond origin/<default>").
8457
+ \`--worktrees\` removes it when idle: by its recorded use when there is
8458
+ one, else by the RECENT ACTIVITY sources above being older than
8459
+ --older-than days (reason recently-used while they are newer, and
8460
+ last-use-unknown when they cannot be read). Automatic maintenance removes it
8461
+ only at disk pressure level 3, once that activity is older than
8462
+ maintenance.pressureProtectMinutes and at least 5 minutes old, whatever
8463
+ the setting; unreadable activity keeps it. Removal re-checks, under the removal
8464
+ locks, that HEAD did not move and the worktree is still idle, and
8465
+ \`stim worktree remove\` still refuses dirty or unpushed work.
8466
+
8467
+ PULL REQUESTS: with fresh state, gc runs one \`gh api graphql\` query per
8468
+ repository that asks for the 20 newest pull requests of each linked worktree's branch, as
7874
8469
  \`gh pr list --head <branch> --state all\` would, and for each worktree
7875
8470
  takes the pull request whose head is HEAD, else one whose head contains
7876
8471
  HEAD. A pull request whose head HEAD is past ("PR #12 merged, and HEAD has
@@ -7888,14 +8483,15 @@ SWEEPING FINISHED WORKTREES
7888
8483
  not looked up. \`stim worktree remove\` makes the same check when
7889
8484
  local-only commits alone would refuse the removal, and accepts a merged
7890
8485
  pull request whose head is or contains HEAD.
7891
- stim gc # report merged worktrees
7892
- stim gc --delete # remove them
8486
+ stim gc # report merged worktrees from local state
8487
+ stim gc --delete # refresh, then remove them
7893
8488
  stim gc --delete --worktrees --older-than 3 # also the clean idle ones
7894
8489
  --cache with --worktrees is refused with STIM_BAD_ARG; run them separately.
7895
8490
  With --delete it runs the \`stim worktree remove\` pipeline, never --force,
7896
8491
  on each removable worktree. That pipeline re-inspects the worktree and
7897
8492
  re-checks use and recent activity under the removal locks, and idleness
7898
- for an idle worktree or an unchanged HEAD for a merged one, then parks
8493
+ for an idle worktree, an unchanged HEAD for a merged one, or both for one
8494
+ with no commits of its own, then parks
7899
8495
  devices through the same teardown and handles the
7900
8496
  branch exactly as a manual \`stim worktree remove\`, which keeps the
7901
8497
  checkout when its HEAD moves while devices are reclaimed. A worktree that
@@ -7927,9 +8523,27 @@ ORPHANED WORKSPACE DIRECTORIES
7927
8523
  it. A directory with a missing or unparseable workspace.json, or a root on
7928
8524
  an unmounted volume, is reported under Skipped and never deleted.
7929
8525
 
8526
+ TMPDIR LEFTOVERS
8527
+ \`gc\` without --cache reports recognized entries directly in the user's TMPDIR
8528
+ under "TMPDIR leftovers", with their count, size and a remedy: Stim temp
8529
+ directories (stim-claim-*, stim-host-*, stim-fakehome-*,
8530
+ stim-hosted-macos-*, stim-server-*, stim-ws-*), xctrace recordings
8531
+ (instruments*.ktrace) and Jest's jest_<uid in base 36> cache. \`gc --delete\` checks
8532
+ them again and deletes a Stim temp directory or recording only when nothing
8533
+ in it changed for 24 hours. It keeps a Stim temp directory that contains or
8534
+ sits inside a registered project, holds a live or unverifiable ownership
8535
+ claim or a .lock directory that is not a claim set, or cannot be listed. It
8536
+ keeps a recording that cannot be read, and every recording while an xctrace
8537
+ or Instruments process runs or the process list cannot be read. Jest's cache and every other entry
8538
+ are never deleted. gc never follows a symlink and never acts outside TMPDIR.
8539
+ With STIM_HOME set it neither reports nor deletes them, because TMPDIR is
8540
+ shared by every Stim home. Automatic maintenance runs the same deletion at disk pressure
8541
+ level 2.
8542
+
7930
8543
  STALE STATUS CACHE ENTRIES
7931
8544
  \`stim status\` caches each folder's size under $STIM_HOME/disk-usage and
7932
- each worktree's pull request under $STIM_HOME/pull-requests. \`worktree
8545
+ each worktree's pull request under $STIM_HOME/pull-requests, which the
8546
+ maintenance worktree check and \`gc --delete\` also write. \`worktree
7933
8547
  remove\` drops the entries of the worktree it removed (its checkout,
7934
8548
  node_modules and workspace folder, and its pull request), not the size
7935
8549
  entries of its owned devices' data folders. \`gc\` reports the
@@ -8076,6 +8690,14 @@ THE MIRROR IMAGE: A STALE DEVICE RECORD
8076
8690
  runs still counts as this home's; run \`gc\` after deleting a Stim AVD by
8077
8691
  hand.
8078
8692
 
8693
+ The machine EAS session ledger (~/.stim/machine/eas/sessions.json) can hold
8694
+ entries that no EAS session ever had, such as ids written by a test
8695
+ fixture. \`gc\` reports an entry under "Stale EAS ledger entries" only when
8696
+ its id is not a UUID AND its workspace root is an absolute path that is
8697
+ missing on a mounted volume. A valid id, an existing root, or a root on an
8698
+ unmounted volume keeps the entry. \`gc --delete\` re-checks each entry and
8699
+ removes it under the ledger lock; it never contacts EAS.
8700
+
8079
8701
  THE ONE CASE GC WILL NOT REAP
8080
8702
  If the config is gone entirely (deleted ~/.stim, or a throwaway
8081
8703
  STIM_HOME), gc cannot tell your stale devices from another config's LIVE
@@ -8287,8 +8909,11 @@ WORKSPACE BUILD OUTPUTS
8287
8909
  and worktree warm record in state.json, lastBuild.startedAt,
8288
8910
  supervisor.startedAt and the mtimes under logs/, so a dev server that keeps
8289
8911
  logging keeps its workspace in use.
8290
- The same time decides \`--older-than\` device reaping, and a workspace with
8291
- no evidence of use keeps its device.
8912
+ A workspace with none of those was last used when its workspace.json was
8913
+ written, which happens once, when the workspace is created; only a
8914
+ workspace with no readable workspace.json has an unknown last use. The
8915
+ same time decides \`--older-than\` device reaping and automatic device
8916
+ deletion, and a workspace whose last use is unknown keeps its device.
8292
8917
  The next build of an unchanged app installs from the shared build cache.
8293
8918
  After a native change the Xcode compilation cache speeds the rebuild, but on
8294
8919
  React Native 0.86 Swift does not use it (explicit modules are off), so that
@@ -8476,7 +9101,7 @@ KEYS STIM READS
8476
9101
  flag overrides this per invocation. Unset means Debug.
8477
9102
  ios.remote "proxy", "eas", or a named approved Mac from
8478
9103
  remote.machines, with the same meaning as --remote.
8479
- "auto" places on an approved Mac when this Mac is full or
9104
+ "auto" places on an approved Mac when this machine is full or
8480
9105
  busy. Unset runs here; "local" runs here even when a
8481
9106
  lower layer says otherwise. See lifecycle hosted-ios.
8482
9107
  ios.projectPath directory under the app holding a bare React Native
@@ -8519,6 +9144,19 @@ KEYS STIM READS
8519
9144
  name: Stim is non-interactive, so it refuses an
8520
9145
  ambiguous identity rather than picking one. It wins
8521
9146
  over ios.signingIdentity.
9147
+ ios.appStoreConnectKeyPath, ios.appStoreConnectKeyId, ios.appStoreConnectIssuerId
9148
+ machine scope only; STIM_ASC_KEY_PATH, STIM_ASC_KEY_ID
9149
+ and STIM_ASC_ISSUER_ID each override their own
9150
+ setting when set and non-empty. The absolute
9151
+ path of an App Store Connect API key (.p8), its key
9152
+ ID and its issuer ID. Only \`stim device provision
9153
+ ios\` reads them, passing them to xcodebuild as
9154
+ -authenticationKeyPath/-authenticationKeyID/
9155
+ -authenticationKeyIssuerID instead of the Xcode
9156
+ account, so provisioning works on a Mac where nobody
9157
+ signs in to Xcode. Set all three or none; Stim never
9158
+ reads the key file or prints its path. Builds never
9159
+ use them.
8522
9160
  ios.lanHost e.g. "192.168.1.42" -- the address a phone uses to
8523
9161
  reach this workspace's Metro on an \`ios --device\`
8524
9162
  Debug run, pinning the interface on a multi-NIC Mac
@@ -8637,6 +9275,18 @@ ${ANDROID_AVD_CONFIG_HELP.map((line) => ` ${line}`).joi
8637
9275
  build: embedded JS, no Metro, cache keyed on the
8638
9276
  variant, and an APK re-pack on cache hits. See
8639
9277
  \`guide lifecycle release\`.
9278
+ android.artifactInputs a native Gradle APK cache declaration:
9279
+ complete: true, ignored: [exact repository-relative files],
9280
+ outputs: [generated repository-relative directories],
9281
+ localFiles: [exact project-relative or absolute files],
9282
+ environment: [variable names]. complete promises all
9283
+ inputs are repeatable and dependencies pinned. Include
9284
+ signing keys and every external or environment input.
9285
+ Missing local files and environment values are hashed
9286
+ as absent. Configured files providers can share the APK
9287
+ and verification receipt. Input values stay local;
9288
+ this is not build-worker transfer authorization.
9289
+ See the native Android guide.
8640
9290
  android.offloadInputs a native Gradle build-worker declaration:
8641
9291
  {"complete":true,"ignored":[],"outputs":["build","app/build"]}.
8642
9292
  complete confirms Git-visible source plus the exact
@@ -8644,7 +9294,7 @@ ${ANDROID_AVD_CONFIG_HELP.map((line) => ` ${line}`).joi
8644
9294
  build. outputs names generated directories reported
8645
9295
  by AGP; recorded outputs can survive worker sync.
8646
9296
  Omitted optional inputs can change build results.
8647
- Stim artifact caching remains unavailable. See
9297
+ Worker APKs remain uncached. See
8648
9298
  \`stim guide lifecycle native-android\` for input,
8649
9299
  privacy and worker requirements.
8650
9300
  android.keystore the keystore a RE-PACKED release APK is signed with,
@@ -8669,16 +9319,16 @@ ${ANDROID_AVD_CONFIG_HELP.map((line) => ` ${line}`).joi
8669
9319
  the debug keystore's fixed "android".
8670
9320
  android.remote "proxy", "eas", or a named approved Mac in
8671
9321
  remote.machines; "auto" places on an approved Mac when
8672
- this Mac is full or busy. Unset runs here; "local"
9322
+ this machine is full or busy. Unset runs here; "local"
8673
9323
  runs here even when a lower layer says otherwise.
8674
9324
  See lifecycle hosted-android.
8675
9325
  remote.easFallback true lets "auto" (ios.remote, android.remote or
8676
9326
  --remote auto) run the simulator or emulator on a
8677
- billed EAS Simulator when this Mac is at its
9327
+ billed EAS Simulator when this machine is at its
8678
9328
  concurrency.maxDevices cap (or has runs queued) and no
8679
- remote.machines Mac takes the run, instead of waiting
9329
+ remote.machines entry takes the run, instead of waiting
8680
9330
  or refusing with STIM_AT_CAPACITY. Default false. A
8681
- busy Mac with a free slot never uses it. Stim checks
9331
+ busy machine with a free slot never uses it. Stim checks
8682
9332
  first that the run could use --remote eas (eas-cli
8683
9333
  with simulator commands, eas simulator:availability,
8684
9334
  agent-device, the default slot, a reachable Metro);
@@ -9036,51 +9686,61 @@ test suites and the end-to-end harness use one -- and a scoped config must not
9036
9686
  leave simulators on the machine it cannot account for. A redirected home that
9037
9687
  wants a pool says so with the variable.
9038
9688
 
9039
- DESKTOP ACCESS TICKETS
9040
- Stim Desktop's Add wizard sets STIM_ACCESS_TICKET for one \`doctor --fix\`
9041
- run. Agents never set it, never run \`stim-server setup\`, never edit
9689
+ SETUP TICKETS
9690
+ A remote machine approves this one when a person there runs
9691
+ \`stim-server setup\` with this machine's setup ticket (32 random bytes, 43
9692
+ base64url characters) in STIM_SETUP_TICKET. Stim Desktop's Add wizard makes the
9693
+ ticket and sets STIM_ACCESS_TICKET for one \`doctor --fix\` run; without it,
9694
+ \`doctor --fix\` makes one and prints the setup command to run on the worker.
9695
+ Agents never set STIM_ACCESS_TICKET, never run \`stim-server setup\`, never edit
9042
9696
  \`remote.*\`, \`server.*\` or \`hosting.*\` for the user, and never approve requests.
9043
- Stim sends the ticket (43 base64url characters) only in build and device-host access
9044
- requests and stores only its SHA-256 ticketHash. With --fix, a still-pending
9045
- request is replaced when its ticketHash is missing or differs from the current
9046
- ticket; matching hashes and approved credentials are preserved.
9697
+ Until setup approves, the worker answers unknown-secret and the entry stays
9698
+ pending; every doctor run tries again. The first connection after approval pins
9699
+ the worker's stim-server key in build-machines.json or device-host-machines.json,
9700
+ and every later connection must prove that key; the tailnet node and address
9701
+ never decide who the worker is. With --fix, a pending entry is replaced when
9702
+ STIM_ACCESS_TICKET names another ticket.
9047
9703
  Approved doctor entries under remoteMachines and deviceHosts include host
9048
9704
  { name, screenRecording, accessibility } when the worker reports it.
9049
9705
 
9050
- REMOTE MACS AND PHONE APP
9051
- When the user mentions slow builds, a busy Mac, a full disk or device limit, or
9706
+ REMOTE MACHINES AND PHONE APP
9707
+ When the user mentions slow builds, a busy machine, a full disk or device limit, or
9052
9708
  wanting updates away from the desk, tell them about Stim Desktop > Settings >
9053
- Remote Macs > Add (a Mac on the tailnet that builds or hosts simulators for
9054
- this one) or Phones > Pair a Phone (the Stim phone app). Stim Desktop also
9055
- suggests these itself, at most once a day and never during a build. Agents only
9056
- tell the user: they never run \`stim-server setup\`, never edit \`remote.*\`,
9057
- \`server.*\` or \`hosting.*\` for the user, and never approve requests.
9058
-
9059
- REMOTE MACS ARE MACHINE-LEVEL
9060
- \`remote.machines\` names the remote Macs this Mac may build on and host owned
9709
+ Remote Machines > Add (another machine on the tailnet that builds for this one,
9710
+ or a Mac that also hosts simulators) or Phones > Pair a Phone (the Stim phone
9711
+ app). Stim Desktop also suggests these itself, at most once a day and never
9712
+ during a build. Agents only tell the user: they never run \`stim-server setup\`,
9713
+ never edit \`remote.*\`, \`server.*\` or \`hosting.*\` for the user, and never
9714
+ approve requests.
9715
+
9716
+ REMOTE MACHINES ARE MACHINE-LEVEL
9717
+ \`remote.machines\` names the remote machines this machine may build on and host owned
9061
9718
  simulator sessions on, by MagicDNS name with an optional serve port (default
9062
9719
  7443). Name each node and port once:
9063
9720
 
9064
9721
  stim settings set remote.machines '["janics-mac-mini"]'
9065
9722
  stim doctor --fix
9066
9723
 
9067
- Automatic membership is separate from approval. In Desktop Settings > Remote Macs,
9068
- use Builds enabled and Simulators enabled for this Mac or a configured remote.
9069
- The equivalent machine settings list the excluded members; both default to []:
9724
+ Automatic membership is separate from approval. In Desktop Settings > Remote
9725
+ Machines, use Builds enabled and Simulators enabled for this machine or a
9726
+ configured remote. The equivalent machine settings list the excluded members;
9727
+ both default to []:
9070
9728
 
9071
9729
  stim settings set remote.buildPoolDisabled '["local"]'
9072
9730
  stim settings set remote.devicePoolDisabled '["janics-mac-mini"]'
9073
9731
  stim settings unset remote.buildPoolDisabled
9074
9732
 
9075
- Use local for this Mac and exact remote.machines entries for remotes, including
9733
+ Use local for this machine and exact remote.machines entries for remotes, including
9076
9734
  case and port. Names are not trimmed; unmatched entries exclude nothing. Copy the
9077
9735
  configured name or use Desktop's switches. Each pool
9078
9736
  must retain local or at least one configured remote already approved for that role.
9079
9737
  Offline approved members count as configured members, but placement still requires
9080
- an available compatible host. Settings refuses removing the last member, including
9081
- removing it from remote.machines. Disabling does not unpair a machine or stop a run.
9738
+ an available compatible host. A remote that tailscale status reports as running an
9739
+ OS other than macOS is never a device-pool member or hosting candidate, since it
9740
+ cannot host; an unknown OS counts as able to host. Settings refuses removing the
9741
+ last member, including removing it from remote.machines. Disabling does not unpair a machine or stop a run.
9082
9742
 
9083
- These settings apply only to new automatic work requested by this Mac. They do not
9743
+ These settings apply only to new automatic work requested by this machine. They do not
9084
9744
  change which work other requesters send to a host. Named placement and --remote-build
9085
9745
  local bypass membership; the default device placement without --remote auto remains
9086
9746
  local. Existing local and hosted device sessions keep their owner. Cache hits remain
@@ -9090,16 +9750,33 @@ local alone does not trigger billed EAS fallback; an existing explicit EAS opt-i
9090
9750
  requires the physical device cap or queue condition. Restore membership before retrying or choose
9091
9751
  an explicit placement. Build and simulator memberships are independent.
9092
9752
 
9093
- A remote Mac is used for a capability only after it grants that approval. Build
9094
- and device-host approvals are separate: a Mac in the list that never granted
9753
+ A remote machine is used for a capability only after it grants that approval. Build
9754
+ and device-host approvals are separate: a machine in the list that never granted
9095
9755
  one is not an error, it is not used for that capability, and doctor reports
9096
9756
  it plainly.
9097
9757
 
9758
+ PAUSE INCOMING WORK
9759
+ On the worker machine, an operator can temporarily refuse new incoming builds or
9760
+ hosted sessions without revoking any clients:
9761
+
9762
+ stim settings set server.buildsPaused true
9763
+ stim settings set server.devicesPaused true
9764
+
9765
+ Both machine settings default to false. The running server reads changes without
9766
+ restarting and rechecks admission after an earlier offer. Running builds and
9767
+ existing iOS, Android and macOS hosted sessions continue; attach, cached artifact
9768
+ retrieval and stop remain available. Local CLI builds and devices are unaffected.
9769
+ These settings apply to every requesting machine, including explicit placement.
9770
+ Desktop's pool switches instead affect only that requester's automatic placement.
9771
+ Set either setting to false to resume that capability. A pause does not make the
9772
+ machine idle: wait for existing work to finish or stop the relevant sessions before
9773
+ benchmarking. Agents change these settings only when the user asks them to.
9774
+
9098
9775
  DEVICE HOSTING
9099
- Only \`doctor --fix\` asks for device-host access. A person on that Mac
9100
- approves the printed id with \`stim-server devices grant <id> --device-host\`.
9101
- A person can also run \`stim-server setup\` on the worker: one node, one ticket,
9102
- one expiry, at most one request per capability, with a per-grant Y/n question (Enter approves) in a
9776
+ Only \`doctor --fix\` asks for device-host access: it makes a setup ticket and
9777
+ prints the command. A person approves by running \`stim-server setup\` on the
9778
+ worker with that ticket: one ticket, one expiry, one record per capability for
9779
+ the key that holds the ticket, with a per-grant Y/n question (Enter approves) in a
9103
9780
  terminal or \`--yes\` otherwise. Agents never run \`stim-server setup\` or
9104
9781
  approve requests. Setup never changes TCC or enables Funnel; an SSH-driven
9105
9782
  run is not offered. Without a terminal or --yes, setup refuses before installing
@@ -9107,10 +9784,10 @@ unless every chosen capability already has a matching approval. Desktop reuse
9107
9784
  requires an existing tailnet route. Ctrl-C or SIGTERM completes the journal,
9108
9785
  releases the setup claim and exits 1; a typed N also exits 1. Hosting grants
9109
9786
  include no read, control or build capability.
9110
- On a remote Mac that hosts, Stim Desktop > Settings > Remote Macs > Running here lists the
9111
- simulators, emulators and apps approved Macs run there, below Macs using this
9112
- Mac. Stop asks for confirmation, ends the session and deletes or parks
9113
- its device on that Mac. Parked sessions remain listed without Stop. The list
9787
+ On a remote Mac that hosts, Stim Desktop > Settings > Remote Machines > Running
9788
+ here lists the simulators, emulators and apps approved machines run there, below
9789
+ Macs Using This Mac. Stop asks for confirmation, ends the session and deletes
9790
+ or parks its device on that Mac. Parked sessions remain listed without Stop. The list
9114
9791
  refreshes every five seconds and stays hidden when the local server does not
9115
9792
  support it.
9116
9793
  $STIM_HOME/device-host-machines.json stores a private token and pinned tailnet
@@ -9120,7 +9797,7 @@ stim settings set ios.remote auto --scope machine (or android.remote; workspace,
9120
9797
  repo and committed scopes override the machine default),
9121
9798
  or per run stim ios --remote auto / stim ios --remote <machine>.
9122
9799
  New Desktop runs pass no --remote flag; recorded hosted sessions keep their
9123
- machine until stim stop. Only devices outside this Mac show "on <machine>"
9800
+ machine until stim stop. Only devices outside this machine show "on <machine>"
9124
9801
  on tiles, workspace pages, viewer toolbars and sidebar rows, with the reason
9125
9802
  as hover text.
9126
9803
  Stim sends tokens only to the pinned node's own tailnet address,
@@ -9146,28 +9823,29 @@ shared daemon only when that agent-device can lease a single app (its
9146
9823
  notice, and no client is handed the Mac's desktop. STIM_AGENT_DEVICE_BIN in
9147
9824
  stim-server's environment names an agent-device binary to use instead of
9148
9825
  ~/.local/bin/agent-device. Hosted iOS uses one daemon per simulator, pinned by
9149
- its UDID policy; both Macs need agent-device 0.21.20 or later. Hosted Android
9826
+ its UDID policy; both machines need agent-device 0.21.20 or later. Hosted Android
9150
9827
  uses one daemon per emulator, pinned by its serial policy, with agent-device
9151
- 0.21.22 or later on both Macs. See guide lifecycle
9828
+ 0.21.22 or later on both machines. See guide lifecycle
9152
9829
  hosted-ios. \`doctor\` on that Mac notes an installed hosted app that runs while
9153
9830
  the setting is \`none\`.
9154
9831
 
9155
9832
  REMOTE BUILDS
9156
- The same \`remote.machines\` list names the Macs that may build for this one:
9833
+ The same \`remote.machines\` list names the machines that may build for this one:
9157
9834
 
9158
9835
  {
9159
9836
  "remote": { "machines": ["janics-mac-mini"] }
9160
9837
  }
9161
9838
 
9162
- \`doctor --fix\`, run in any app directory, asks each named Mac for build access and pins its tailnet
9163
- node in $STIM_HOME/build-machines.json. A person approves the request on that
9164
- Mac with \`stim-server devices grant <id> --build\`; doctor prints the id.
9165
- A person can instead run \`stim-server setup\` on the worker with a node,
9166
- ticket and expiry, under the approval rule above. Build never includes read
9167
- or control.
9168
- Stim connects to a named Mac only while its name still belongs to the pinned
9839
+ \`doctor --fix\`, run in any app directory, makes a setup ticket for each named
9840
+ machine and prints the \`stim-server setup\` command a person runs on that
9841
+ machine to approve this one, under the approval rule above. The next doctor run
9842
+ pins the worker's stim-server key in $STIM_HOME/build-machines.json. A build grant is full access
9843
+ to the account stim-server runs as: project code runs there unsandboxed, so it
9844
+ can grant itself read and control. Revoking it does not undo what that code
9845
+ already did. Grant it only to machines you trust.
9846
+ Stim connects to a named machine only while its name still belongs to the pinned
9169
9847
  node and never sends the token to another node. \`doctor\` reports each
9170
- remote Mac's build pairing state and, for an approved one, asks it for one build
9848
+ remote machine's build pairing state and, for an approved one, asks it for one build
9171
9849
  offer and lists every reason it would not take this app's iOS build: no
9172
9850
  answer, another Stim build, CPU, Xcode, simulator SDK or CocoaPods, no
9173
9851
  Bundler for an app whose Gemfile.lock pins CocoaPods, no iPhone simulator on
@@ -9182,12 +9860,12 @@ configured names case-insensitively, with port 7443 when omitted; reports use
9182
9860
  the configured entry.
9183
9861
 
9184
9862
  auto follows remote.buildMode, considering only enabled automatic pool members
9185
- local builds only on this Mac for this invocation
9863
+ local builds only on this machine for this invocation
9186
9864
  name requires a matching entry in remote.machines, already paired and
9187
- approved for builds; ignores remote.buildMode and this Mac's load/slot gating
9865
+ approved for builds; ignores remote.buildMode and this machine's load/slot gating
9188
9866
 
9189
- A name never prompts for pairing, tries another remote Mac or falls back here.
9190
- Any refusal or failure is STIM_OFFLOAD_REFUSED with the remote Mac and reason.
9867
+ A name never prompts for pairing, tries another remote machine or falls back here.
9868
+ Any refusal or failure is STIM_OFFLOAD_REFUSED with the remote machine and reason.
9191
9869
  Device, Release/non-Debug, --remote, Android CAS compiler builds, builds with
9192
9870
  the build cache off, and an unknown simulator runtime cannot build there and
9193
9871
  refuse on a cache miss. Invalid values, unlisted names and unpaired names refuse
@@ -9195,12 +9873,11 @@ at setup before consulting the cache; they create no build record or failed-run
9195
9873
  stats. A listed paired name with a cache hit compiles nothing, contacts no worker
9196
9874
  and records buildMachine with builtOn absent. Build failures retain the selected
9197
9875
  value; builtOn is here or the configured machine entry when compilation ran.
9198
- Prebuild and pod install still run on this Mac before a named offload. A refusal
9876
+ Prebuild and pod install still run on this machine before a named offload. A refusal
9199
9877
  starts no local xcodebuild, Gradle or SwiftPM compile.
9200
9878
  Run stim doctor --fix to ask for build access if not paired. A person on the
9201
- worker finds the id with stim-server devices and approves it with
9202
- stim-server devices grant <id> --build, or runs stim-server setup there with
9203
- the client node, ticket and expiry. Check stim settings get remote.machines,
9879
+ worker approves it by running the stim-server setup command doctor printed, with
9880
+ its ticket and expiry. Check stim settings get remote.machines,
9204
9881
  or rerun with --remote-build auto or --remote-build local. Ctrl-C cancels
9205
9882
  the remote build without a local xcodebuild, Gradle or SwiftPM compile.
9206
9883
 
@@ -9211,23 +9888,23 @@ Copyable agent prompt:
9211
9888
  With remote.build auto, \`remote.buildMode\` decides where an iOS simulator Debug build or an Android
9212
9889
  emulator debug build or a stim macos SwiftPM Debug build compiles:
9213
9890
 
9214
- auto (default) here while this Mac has capacity: a free
9891
+ auto (default) here while this machine has capacity: a free
9215
9892
  \`concurrency.maxBuilds\` slot (always, with no build limit) and a
9216
9893
  load per core under \`server.maxLoadPerCore\`. Otherwise on a
9217
- remote Mac that accepts the build and is expected to be faster:
9218
- while every slot here is busy, any remote Mac that accepts; while only
9894
+ remote machine that accepts the build and is expected to be faster:
9895
+ while every slot here is busy, any remote machine that accepts; while only
9219
9896
  the load is high, one whose load per core is lower than this
9220
- Mac's. A Mac too old to report its load counts only while every
9897
+ machine's. A machine too old to report its load counts only while every
9221
9898
  slot here is busy.
9222
- force on a remote Mac whenever one accepts it
9899
+ force on a remote machine whenever one accepts it
9223
9900
  off here when local remains enabled in the automatic build pool
9224
9901
 
9225
- Load per core is the 5-minute load average divided by the CPU count; a Mac's
9226
- native builds are its Stim runs in prebuild, pods or compile on that Mac, not
9902
+ Load per core is the 5-minute load average divided by the CPU count; a machine's
9903
+ native builds are its Stim runs in prebuild, pods or compile on that machine, not
9227
9904
  the ones it offloaded.
9228
9905
  \`server.maxLoadPerCore\` (default 2) is also the busy threshold for
9229
- automatic iOS and Android device placement. It is the load per core at which a Mac
9230
- counts as saturated, both here and on a remote Mac.
9906
+ automatic iOS and Android device placement. It is the load per core at which a machine
9907
+ counts as saturated, both here and on a remote machine.
9231
9908
 
9232
9909
  STIM_REMOTE_BUILD_MODE overrides it for one command. Device, Release and
9233
9910
  \`--remote eas|proxy\` builds, Android builds with the Apple Clang CAS compiler cache,
@@ -9239,17 +9916,25 @@ cache on the worker. It skips JavaScript dependencies, prebuild and pods; see
9239
9916
  stim guide macos. macOS artifacts are not cached.
9240
9917
  An offloaded iOS/Android build first runs prebuild (and \`pod install\` for iOS) here,
9241
9918
  then asks every paired machine what it can build. It takes one whose Stim
9242
- build matches this Mac and CPU matches the target simulator, with enough disk, that does not decline,
9919
+ build matches this Mac and, for iOS and macOS, CPU matches the target simulator, with enough disk, that does not decline,
9243
9920
  preferring the one that already holds this repository, then the least loaded,
9244
9921
  and moves to the next one in that order when a machine that offered fails the
9245
9922
  sync or refuses to start the build.
9923
+ After a local and configured-provider miss, native Xcode simulator builds can
9924
+ reuse an exact completed archive from an enabled paired worker. Its full key
9925
+ includes source, Xcode, SDK, architecture and target options. A hit skips sync
9926
+ and compilation even while the worker is busy or this machine has room. Missing or
9927
+ corrupt archives follow normal placement. Off mode and an explicit local build
9928
+ do not query peer caches. Older workers continue normal placement. This peer
9929
+ lookup covers native Xcode worker artifacts, not compiler or dependency caches.
9246
9930
  For iOS the machine's Xcode and simulator SDK must match, and it needs an
9247
9931
  iPhone simulator on the target runtime. Its project-selected CocoaPods must match too, unless
9248
- the app's Gemfile.lock pins CocoaPods: both Macs then run that version through
9932
+ the app's Gemfile.lock pins CocoaPods: both machines then run that version through
9249
9933
  bundler, so the machine needs only Bundler on its stim-server PATH and
9250
9934
  installs the pinned gems itself on the first build. The comparison selects
9251
- the app's .ruby-version when installed, and otherwise the GEM_HOME, GEM_PATH and Ruby-related PATH entries of the Mac'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.
9252
- For Android its JDK major
9935
+ 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.
9936
+ For Android its CPU may differ, since the APK's ABIs come from the NDK, so an
9937
+ x86_64 Linux or Windows machine running stim-server can build for an Apple silicon Mac. Its JDK major
9253
9938
  version must match, and its Android SDK must hold the NDK, build-tools and
9254
9939
  compile platform that the project's React Native version names in
9255
9940
  gradle/libs.versions.toml; Gradle and AGP come from the synced project. When
@@ -9259,7 +9944,7 @@ reason, such as \`janics-mac-mini: busy (load at or above 2/core; load
9259
9944
  \`git ls-files -co --exclude-standard\` lists (the machine keeps only files it
9260
9945
  lacks, so an unignored secret such as \`.env\` is sent too), builds with
9261
9946
  Stim's own code there, refuses unless the fingerprint there equals this one,
9262
- and brings the \`.app\` or APK back. An Android build there uses this Mac's
9947
+ and brings the \`.app\` or APK back. An Android build there uses this machine's
9263
9948
  variant, target ABI, Gradle build cache, PCH and compiler cache choices, so
9264
9949
  the APK matches the ABI-narrowed cache key. Stim checks the archive's sha256
9265
9950
  and fingerprints this checkout again before it stores the app under the
@@ -9277,11 +9962,11 @@ interrupted while it reconnects, leaves it until those 5 minutes pass. With auto
9277
9962
  \`--json\` facts record the reason as offloadFallback. Offloading holds no
9278
9963
  local build slot; that fallback takes one. \`stim stats\` keeps where each
9279
9964
  compiling build ran and why (\`guide facts stats\`).
9280
- An offloaded app lands only in this Mac's build cache, not in a remote cache
9965
+ An offloaded app lands only in this machine's build cache, not in a remote cache
9281
9966
  provider. A project whose xcodebuild changes its own fingerprinted inputs
9282
9967
  fails the offload fingerprint check: auto builds here; a named placement refuses.
9283
9968
 
9284
- On a remote Mac that builds for others, stim-server keeps each client's checkouts, dependencies,
9969
+ On a remote machine that builds for others, stim-server keeps each client's checkouts, dependencies,
9285
9970
  DerivedData, compilation cache, ccache and Gradle home under
9286
9971
  \`server.workerRoot\` (absolute; default $STIM_HOME/build-worker), one area
9287
9972
  and Stim home per client and repository, and one Gradle home per client. It
@@ -9290,21 +9975,23 @@ less than 10 GB free, while its own native builds and the offloaded one fill
9290
9975
  its \`concurrency.maxBuilds\`, or while its load per core is at or above its
9291
9976
  \`server.maxLoadPerCore\`, and reports the reason in its offer. It boots and
9292
9977
  installs nothing. While an offloaded build runs there, it holds one of that
9293
- Mac's \`concurrency.maxBuilds\` slots, so a local Stim build there waits for it
9978
+ machine's \`concurrency.maxBuilds\` slots, so a local Stim build there waits for it
9294
9979
  and counts it as busy; the slot is freed when the build ends, is cancelled or
9295
9980
  its process is gone. Android builds there run on the JDK in stim-server's
9296
9981
  JAVA_HOME (else the macOS default JDK) with the SDK in its ANDROID_HOME (else
9297
- ~/Library/Android/sdk). The client's Gradle daemon stays warm there for
9982
+ ~/Library/Android/sdk, or ~/Android/Sdk on Linux). The client's Gradle daemon stays warm there for
9298
9983
  \`server.gradleDaemonIdleMinutes\` (default 30; 0 stops it when each build
9299
9984
  ends; a new value applies from the next daemon) and never holds a build
9300
9985
  slot. While none of the client's builds runs, stim-server stops it sooner
9301
9986
  when a build is cancelled, the client is revoked, or the machine has less
9302
9987
  than 2 GB of memory available, and deleting the client's directory under the
9303
9988
  worker root stops it too.
9304
- \`stim-server service install [--serve]\` on the remote Mac keeps stim-server
9305
- running as a login LaunchAgent; \`--path-prepend <dir>\` and \`--env KEY=VALUE\`
9306
- pin a PATH entry or variable such as a private CocoaPods that stim-server's
9307
- login-shell environment would otherwise replace.
9989
+ \`stim-server service install [--serve]\` on a remote machine keeps stim-server
9990
+ running as a login LaunchAgent on macOS, a systemd user service on Linux
9991
+ (\`sudo loginctl enable-linger <user>\` keeps it running after logout; install
9992
+ prints that command and never runs it), or a logon scheduled task on Windows; \`--path-prepend <dir>\` and \`--env KEY=VALUE\`
9993
+ pin a PATH entry or variable, such as a private CocoaPods, that stim-server's
9994
+ toolchain discovery does not find.
9308
9995
  \`stim-server service update --release <version>\` there moves the service to
9309
9996
  that exact stim-server release from the public npm registry once npm verifies
9310
9997
  its integrity and registry signatures; \`--from <dir>\` installs the packed
@@ -9312,15 +9999,15 @@ packages of a checkout instead, with the same signature check on each npm
9312
9999
  dependency but none for Stim's own packages. It waits for offloaded builds and hosted
9313
10000
  sessions to finish, restarts the job, and switches back when the new server
9314
10001
  exits or does not answer within 90 seconds; \`stim-server service rollback\` returns
9315
- to the previous server. A client Mac approved for builds or device hosting
10002
+ to the previous server. A client machine approved for builds or device hosting
9316
10003
  there can ask for the same update over its tailnet connection: an npm release
9317
10004
  always, and its own packed build only while \`server.acceptClientBuilds\` is
9318
10005
  true on that machine (default false).
9319
10006
  Replacing a loaded service waits up to 45 seconds for its existing listeners to
9320
10007
  release the port. Uninstall does not wait for the port. If stopping the old job
9321
- or starting the new one fails, install restores the previous plist and attempts
9322
- to restart a previously loaded LaunchAgent; if it cannot, it prints the
9323
- launchctl bootout/bootstrap remedy. A running LaunchAgent does not prove readiness; check \`stim-server service status\`
10008
+ or starting the new one fails, install restores the previous plist or unit and attempts
10009
+ to restart a previously running service; if it cannot, it prints the
10010
+ launchctl bootout/bootstrap or systemctl --user daemon-reload/restart remedy. A running service does not prove readiness; check \`stim-server service status\`
9324
10011
  and its reported log. The server listens before it touches the Stim home; a
9325
10012
  read-only child process reads the Stim home, server and recording directories,
9326
10013
  and until it returns \`/health\` answers 503 with \`startup.state\` pending.
@@ -9352,7 +10039,7 @@ stim-server records the screens of owned simulators, emulators and the
9352
10039
  Stim-owned Chrome page while an automation tool drives them or a paired client
9353
10040
  watches them, so the phone app can scrub back through them. It keeps the last
9354
10041
  15 minutes of footage per device under $STIM_HOME/workspaces/<id>/recordings/,
9355
- on this Mac only, and serves it only to paired clients. \`recording.enabled\`
10042
+ on this machine only, and serves it only to paired clients. \`recording.enabled\`
9356
10043
  turns it off:
9357
10044
 
9358
10045
  stim settings set recording.enabled false --scope machine
@@ -9366,6 +10053,19 @@ them within seconds, as soon as \`stim status\` shows recording.enabled false
9366
10053
  for them. Turning it back on records from then on. See \`guide cleanup\` for
9367
10054
  how recordings are removed otherwise.
9368
10055
 
10056
+ HIDE A WORKSPACE IN THE APPS
10057
+ \`workspace.hidden\` is a boolean, false by default, set only at workspace
10058
+ scope, so it needs no --scope. Run it from the workspace:
10059
+
10060
+ stim settings set workspace.hidden true
10061
+ stim settings unset workspace.hidden
10062
+
10063
+ \`stim status --json\` then reports hidden: true on that environment, and
10064
+ clients that read it list the workspace under Hidden. The next start, ios,
10065
+ android, web, macos, reload or worktree warm in the workspace clears it, so
10066
+ a workspace in use is never hidden. \`worktree remove\` drops it with the
10067
+ workspace.
10068
+
9369
10069
  STIM NEEDS NO PROJECT CHANGES TO RUN
9370
10070
  Nothing above is required to use Stim. The performance caches that used to
9371
10071
  be setup steps are supplied by Stim on the command lines it composes itself:
@@ -9432,8 +10132,9 @@ The example shows the defaults. Full setting names and behavior:
9432
10132
  optimizations.remoteBuildCache
9433
10133
  false skips both optional remote artifact providers, including loading
9434
10134
  their modules and authentication. Local artifact caching stays enabled.
9435
- Stim ships no network provider or hosted cache. Remote artifact reuse
9436
- requires a provider configured through cache.provider or Expo.
10135
+ Install @stim-cli/cache-s3 and select it through cache.provider for S3-compatible storage,
10136
+ including Cloudflare R2, or configure another provider through Expo.
10137
+ Stim does not run a hosted cache service; credentials use the AWS SDK chain.
9437
10138
  optimizations.releaseBundleSwap
9438
10139
  false always builds Release from source, including the current JS;
9439
10140
  it never installs an old embedded bundle. Fresh artifacts can still store.
@@ -9503,7 +10204,9 @@ PCH mode matches; disable artifact caching too to force native compilation. Lega
9503
10204
  cannot key these compiler profiles, so Stim skips that tier for custom profiles
9504
10205
  and Android CAS. Providers implementing the Stim key contract remain usable.
9505
10206
  Android compiler/PCH profiles also get separate CMake staging directories under
9506
- <module>/.cxx/stim-<profile>, or under the project's custom staging root. Switching
10207
+ <module>/.cxx/stim-<profile> (<module>/.cxx/<first 4 profile characters> on
10208
+ Windows, where every character counts against MAX_PATH), or under the project's
10209
+ custom staging root. Switching
9507
10210
  backends in Stim selects the matching directory without deleting previous builds.
9508
10211
  These directories accumulate across profile changes and shim upgrades. Stim
9509
10212
  does not prune them; remove an obsolete generated profile only with all native
@@ -9592,7 +10295,11 @@ FileStore shards across 256 directories, a build cache is keyed
9592
10295
  platform instead of one entry. Pass prune: 'atomic' for a cache whose index
9593
10296
  references its own data (an LLVM CAS): it is then left alone by --older-than
9594
10297
  and emptied whole only by 'gc --delete --cache all'.
9595
- Registration is idempotent and keyed on the directory.`
10298
+ Registration is idempotent and keyed on the directory.
10299
+ dir must be absolute (a leading ~ is expanded) and dedicated to the cache.
10300
+ register throws for a path that is or contains the home directory, the system
10301
+ temporary directory, the Stim config directory or a filesystem root, and gc
10302
+ never prunes or empties such a directory.`
9596
10303
  };
9597
10304
  //#endregion
9598
10305
  //#region src/guide/web.ts
@@ -9955,8 +10662,11 @@ attempt falls back here. Placement and failure reasons are in build logs.
9955
10662
 
9956
10663
  AGENT ACTIONS
9957
10664
 
9958
- Launch through stim macos first. Use its isolated bundleId from the JSON
9959
- launch record or status --json:
10665
+ Launch through stim macos first. Use its isolated bundleId from the JSON launch
10666
+ record or status --json. stim macos waits, for up to 20 seconds, until macOS
10667
+ LaunchServices lists the app, so agent-device open attaches to it instead of
10668
+ starting a second copy. If it is still unlisted, stim macos warns and returns;
10669
+ wait before opening. A --remote launch skips the wait:
9960
10670
 
9961
10671
  agent-device open <bundleId> --platform macos --surface app --foreground
9962
10672
  agent-device click <ref> --settle
@@ -10106,11 +10816,11 @@ used.
10106
10816
  ON ANOTHER MAC
10107
10817
 
10108
10818
  stim macos --remote <machine> builds the Debug app on this Mac and runs it on an
10109
- approved remote Mac over the tailnet, without SSH. The machine must be listed
10110
- in remote.machines and approved: stim doctor --fix asks it, a person on that
10111
- Mac runs stim-server devices grant <id> --device-host or stim-server setup
10112
- with that client node, ticket and expiry (guide settings), and stim doctor then
10113
- records the approval. Stim connects only to the machine's pinned tailnet node.
10819
+ approved remote Mac over the tailnet, without SSH. The Mac must be listed
10820
+ in remote.machines and approved: stim doctor --fix makes a setup ticket, a
10821
+ person on that Mac runs stim-server setup with that ticket and expiry (guide
10822
+ settings), and stim doctor then pins that Mac's stim-server key. Stim goes ahead
10823
+ only with the stim-server that proves it holds that key.
10114
10824
  A refusal or an unreachable host fails the command; it never launches here
10115
10825
  instead.
10116
10826
 
@@ -10169,7 +10879,7 @@ approved host that lacks them. A server started by Stim Desktop uses Desktop's
10169
10879
  grants.
10170
10880
 
10171
10881
  Hosted delivery carries the staged bundle with declared resources and compiled assets.
10172
- When offload built it on the host's own tailnet node, the host copies the files
10882
+ When offload built it on the host's own stim-server, the host copies the files
10173
10883
  from the build it kept for this Mac instead of receiving them again, admitting
10174
10884
  only bytes that match the bundle's digests here. That needs both the build and
10175
10885
  the device-host approval for this Mac. Files the host cannot take, an older
@@ -10262,7 +10972,7 @@ const TUTORIAL_ASKS = {
10262
10972
  build: "Make the title purple in the Stim tutorial app and check it on the simulator.",
10263
10973
  parallel: "While that builds, make the Tap me button green in a new worktree and check it on the simulator.",
10264
10974
  finish: "I'm done with these experiments in {base} and don't need the changes. Stop the apps and remove the worktrees {worktrees}, and keep the clone. Follow stim guide tutorial finish.",
10265
- delete: "Remove the Stim tutorial: remove its worktrees and the clone at {base} with stim worktree remove, then delete {base}. Follow stim guide tutorial delete.",
10975
+ delete: "Remove the test app: run stim worktree remove on {base}, then delete the folder. Follow stim guide tutorial delete.",
10266
10976
  share: `Open a pull request to ${TUTORIAL_REPO} with my title color change, with before and after screenshots from the simulator. See stim guide tutorial share.`,
10267
10977
  retry: "The first iOS build of the tutorial app in {tour} failed. Find out why and run it on iOS again."
10268
10978
  };
@@ -10275,7 +10985,7 @@ outside the user's own projects.`;
10275
10985
  const local = `The tutorial runs on this Mac. Every time you run the tutorial app on iOS
10276
10986
  from a worktree of {base}, use stim ios --remote local --remote-build local,
10277
10987
  even when the user's settings would place the device or the build on another
10278
- Mac. Only an explicit request to build on another Mac changes the build, with
10988
+ machine. Only an explicit request to build on another machine changes the build, with
10279
10989
  stim ios --remote local --remote-build "<machine>"; the device stays here.`;
10280
10990
  const screenshots = `Before and after screenshots, for a pull request: in each change's worktree,
10281
10991
  run the app on iOS before you edit anything, open it with agent-device and save
@@ -10371,8 +11081,8 @@ ${screenshots}
10371
11081
 
10372
11082
  Check each change on the device with agent-device in that same session, after
10373
11083
  the after screenshot, so Stim Desktop records your actions and the user can
10374
- replay them: tap tap-button, toggle dark-accent-switch, type a name in
10375
- name-input, then take a screenshot (agent-device screenshot
11084
+ replay them: tap tap-button, toggle dark-accent-switch, pick a size
11085
+ (size-s, size-m or size-l), then take a screenshot (agent-device screenshot
10376
11086
  .expo/screenshots/checked.png) and confirm the change. Refs expire after each
10377
11087
  action: run agent-device snapshot -i before every action and use the ref it
10378
11088
  reports for that testID. When agentDevice.installed is false in stim status
@@ -10598,7 +11308,7 @@ function renderIndex(version) {
10598
11308
  return lines.join("\n");
10599
11309
  }
10600
11310
  function guideCommand(program, version, status = (running) => guideStatus({
10601
- projectRoot: findProjectRoot(process.cwd()),
11311
+ projectRoot: findProjectRoot$1(process.cwd()),
10602
11312
  running
10603
11313
  })) {
10604
11314
  program.command("guide [topic] [section]").description("Print reference documentation for THIS version of Stim (topics: " + topicNames().join(", ") + "). A topic with sections prints its section index; name a section to print only it, e.g. `stim guide errors STIM_NO_METRO` or `stim guide lifecycle builds`. Generated by the binary, so it cannot drift from the installed CLI.").action(async (topic, section) => {