stim 1.25.0 → 1.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. package/README.md +1 -1
  2. package/dist/{acquire-artifact-zA4S7RHE.mjs → acquire-artifact-DVsvSwf7.mjs} +1 -1
  3. package/dist/{act-BbHS3oV8.mjs → act-C-BqJEpu.mjs} +157 -35
  4. package/dist/{activity-BpjwrQXU.mjs → activity-Lnb_EoRY.mjs} +4 -5
  5. package/dist/{agent-device-usage-output-Bnkerfod.mjs → agent-device-usage-output-DbXy5tg8.mjs} +4 -4
  6. package/dist/{android-saBeB4lr.mjs → android-B5P81yI-.mjs} +1 -1
  7. package/dist/{android-CNZgRC-4.mjs → android-CSbLRD53.mjs} +5 -5
  8. package/dist/{android-BGwuK3Mc.mjs → android-CiNQw3kq.mjs} +73 -49
  9. package/dist/{android-Cjcj8MCw.mjs → android-DTAcRwBC.mjs} +81 -34
  10. package/dist/{android-cas-BVqh2Zep.mjs → android-cas-BTVhZ0Qs.mjs} +4 -4
  11. package/dist/{android-project-CTYaETc0.d.mts → android-project-DUGMbq28.d.mts} +55 -1
  12. package/dist/api.d.mts +8 -5
  13. package/dist/api.mjs +27 -27
  14. package/dist/apk-signed-tail-B46U9hXc.mjs +142 -0
  15. package/dist/{artifact-Co8zixVx.mjs → artifact-BxREhAaV.mjs} +23 -18
  16. package/dist/{artifact-DN8JNwqS.mjs → artifact-CsAP6AGK.mjs} +35 -20
  17. package/dist/{attempt-DW-mQqM7.mjs → attempt-ClyJlZPT.mjs} +4 -2
  18. package/dist/{browser-web-CCgZsHmI.mjs → browser-web-rYPyqhTf.mjs} +11 -12
  19. package/dist/{budget-HyShrIpL.mjs → budget-k2mZGvvk.mjs} +196 -33
  20. package/dist/{build-CAiw8Ezm.mjs → build-DPhApsjV.mjs} +26 -23
  21. package/dist/{build-cache-CKCzxzxA.mjs → build-cache-DrXNCy3T.mjs} +103 -21
  22. package/dist/{build-plan-zCuHGlA8.mjs → build-plan-DrMMdayj.mjs} +5 -5
  23. package/dist/{cache-manifest-BPXRONJz.mjs → cache-manifest-BEZ9Bpl1.mjs} +1 -1
  24. package/dist/cache-manifest.mjs +1 -1
  25. package/dist/{ccache-h1FKhZeg.mjs → ccache-Bx5TbxwG.mjs} +6 -6
  26. package/dist/{cli-C4w607Pz.mjs → cli-BBKFGOFl.mjs} +19 -19
  27. package/dist/cli.mjs +1 -1
  28. package/dist/{client-BQsmyp_-.mjs → client-D9CaRks-.mjs} +36 -61
  29. package/dist/collector-run.mjs +4 -4
  30. package/dist/{command-output-BBvS1H6v.mjs → command-output-BF5AR2dL.mjs} +1 -1
  31. package/dist/{config-BZhXY15H.mjs → config-BktkY_zC.mjs} +321 -15
  32. package/dist/{launch-DscF1QfM.mjs → dependencies-Dw6wfQdv.mjs} +258 -255
  33. package/dist/{dependency-state-BwNCYG5Q.mjs → dependency-state-DFB41vfr.mjs} +1 -1
  34. package/dist/{dev-client-CHoXXZeg.mjs → dev-client-DXR_tPVY.mjs} +2 -2
  35. package/dist/{device-B9FqTBYW.mjs → device-BK-zwyd_.mjs} +13 -13
  36. package/dist/{device-F5-4z9ef.mjs → device-BcYeHSFu.mjs} +14 -14
  37. package/dist/{device-capacity-ZYaRALv5.mjs → device-capacity-DCSvUF0l.mjs} +10 -17
  38. package/dist/device-host-worker.mjs +11 -13
  39. package/dist/{device-ios-DkHBrmvH.mjs → device-ios-BLGfXQeK.mjs} +49 -17
  40. package/dist/{device-lease-run-ogEeJ62K.mjs → device-lease-run-BmvIl7bI.mjs} +4 -4
  41. package/dist/{device-pool-BIg8MU8l.mjs → device-pool-Dluix4H3.mjs} +3 -3
  42. package/dist/{doctor-k3P3PizH.mjs → doctor-Bhg1ii70.mjs} +14 -14
  43. package/dist/{doctor-A_Nr_fDe.mjs → doctor-Cp-BhzTS.mjs} +1 -1
  44. package/dist/{doctor-Bm8TVpGn.mjs → doctor-OnwP94ne.mjs} +72 -23
  45. package/dist/{doctor-fingerprint-zlujA1Gs.mjs → doctor-fingerprint-D3a04Mhu.mjs} +8 -2
  46. package/dist/doctor-fingerprint.mjs +1 -1
  47. package/dist/{doctor-watchman-BovI1y3-.mjs → doctor-watchman-BStK05nt.mjs} +4 -4
  48. package/dist/{eas-build-CyDKNTBP.mjs → eas-build-Dsj93iM9.mjs} +2 -2
  49. package/dist/{eas-fallback-gaZi-dJL.mjs → eas-fallback-FwkeqqGi.mjs} +10 -10
  50. package/dist/{eas-session-ledger-BMCA1gdO.mjs → eas-session-ledger-B5vygTiD.mjs} +5 -5
  51. package/dist/{error-diagnostics-B1Mj_-7h.mjs → error-diagnostics-BOOm9Db7.mjs} +8 -8
  52. package/dist/{gc-eaz403W6.mjs → gc-C_79zvz4.mjs} +2 -2
  53. package/dist/{gc-B9jcnmIk.mjs → gc-ldgr-hXU.mjs} +74 -43
  54. package/dist/{guide-DR0K0xW9.mjs → guide-D7K0X6KG.mjs} +272 -52
  55. package/dist/{hosted-android-Bvb2-ZxD.mjs → hosted-android-Bk54ej6n.mjs} +3 -3
  56. package/dist/{hosted-client-rsRqysJc.mjs → hosted-client-fHO8yqzc.mjs} +5 -5
  57. package/dist/{hosted-ios-RtVcJH3u.mjs → hosted-ios-D55QhYaD.mjs} +1 -1
  58. package/dist/{hosted-logs-CWKGYWNX.mjs → hosted-logs-BfbmDM4M.mjs} +17 -11
  59. package/dist/hosted-logs-sync-B0pvmOk-.mjs +195 -0
  60. package/dist/{hosted-macos-d0pv9Tth.mjs → hosted-macos-CPl0OZ9h.mjs} +5 -5
  61. package/dist/{hosted-native-BxTizGU2.mjs → hosted-native-CpDB3-PO.mjs} +6 -7
  62. package/dist/{idle-shutdown-BIyfl0Zj.mjs → idle-shutdown-FfxL1KdG.mjs} +22 -9
  63. package/dist/{ios-Dvt97HgQ.mjs → ios-B7-lToDu.mjs} +8 -8
  64. package/dist/{ios-BQzvJlzw.mjs → ios-WxpDJ5T1.mjs} +72 -5
  65. package/dist/{ios-device-CKSvk0bu.mjs → ios-device-CsyryvpT.mjs} +1 -1
  66. package/dist/{ios-BRVM8b0P.mjs → ios-ftxdQbJS.mjs} +74 -46
  67. package/dist/{ios-project-DCUDo06I.mjs → ios-project-Ty5pHl9O.mjs} +6 -6
  68. package/dist/{ios-state-CT9nV5SP.mjs → ios-state-Cw7-WQf2.mjs} +1 -1
  69. package/dist/{js-swap-Z9HbC4cj.mjs → js-swap-BMhXYVDS.mjs} +4 -4
  70. package/dist/{launch-D0lPvFFu.mjs → launch-DOp8RjDz.mjs} +31 -26
  71. package/dist/{logs-BsIRre8x.mjs → logs-dQDki6Tt.mjs} +9 -173
  72. package/dist/{logs-Crs9y34Z.mjs → logs-s1dRIG1E.mjs} +85 -36
  73. package/dist/{macos-CcUlJPMu.mjs → macos-B0VEV3Gf.mjs} +1 -1
  74. package/dist/{macos-D477nfzH.mjs → macos-DBBPNnIA.mjs} +18 -19
  75. package/dist/macos-run.mjs +1 -1
  76. package/dist/maintenance-run.mjs +28 -11
  77. package/dist/{metro-1pJgCX4U.mjs → metro-CjC10t6E.mjs} +2 -2
  78. package/dist/{metro-gateway-Bp9gJQAN.mjs → metro-gateway-B6_PgD0L.mjs} +2 -3
  79. package/dist/{named-ports-BVbn8SvC.mjs → named-ports-BG0j1X4g.mjs} +2 -2
  80. package/dist/{native-android-BvMizJn1.mjs → native-android-CL2NGiik.mjs} +17 -17
  81. package/dist/{native-gradle-artifact-inputs-BJscK4vN.mjs → native-gradle-artifact-inputs-btSUulme.mjs} +3 -3
  82. package/dist/{native-run-D5OJsGwA.mjs → native-run-BfIDFKLH.mjs} +2 -3
  83. package/dist/{native-runtime-B9w_Qyz3.mjs → native-runtime-Dq3RBcON.mjs} +6 -7
  84. package/dist/{native-xcode-inputs-Cv4l-obn.mjs → native-xcode-inputs-BW7H8JGu.mjs} +2 -2
  85. package/dist/{native-xcode-ios-DGrg90hE.mjs → native-xcode-ios-CBXJAxfC.mjs} +15 -16
  86. package/dist/offload-worker.d.mts +3 -1
  87. package/dist/offload-worker.mjs +31 -21
  88. package/dist/{ownership-jxxz-19m.mjs → ownership-BjYvMNX_.mjs} +2 -2
  89. package/dist/{phone-reverses-CYb7RRAx.mjs → phone-reverses-lmj6kcPm.mjs} +3 -3
  90. package/dist/{placement-log-DjOXJRIH.mjs → placement-log-DIFPQAXp.mjs} +1 -1
  91. package/dist/{plan-placement-BqKgKHCi.mjs → plan-placement-IiiSQhtz.mjs} +3 -3
  92. package/dist/{ports-DtS1Amb0.mjs → ports-C2_w1Z0Z.mjs} +3 -3
  93. package/dist/prebuild-gmouvmJZ.mjs +1247 -0
  94. package/dist/{project-BiC68mIp.mjs → project-BUu6Is4k.mjs} +15 -16
  95. package/dist/{react-native-android-DRYqoJc4.mjs → react-native-android-Cgg99REN.mjs} +47 -33
  96. package/dist/{react-native-build-CwZB-agu.mjs → react-native-build-oNwqF5Wb.mjs} +7 -7
  97. package/dist/{react-native-doctor-BCrytK7h.mjs → react-native-doctor-BhA3KUte.mjs} +49 -8
  98. package/dist/{react-native-ios-Cr1buNFo.mjs → react-native-ios-B-nf4rjN.mjs} +37 -23
  99. package/dist/{recordings-BaoQ0t-J.mjs → recordings-BldzVKq0.mjs} +1 -1
  100. package/dist/{reload-Beftw2X3.mjs → reload-C9OduiNc.mjs} +1 -1
  101. package/dist/{reload-FbSgMF8x.mjs → reload-CCR7KC_l.mjs} +14 -14
  102. package/dist/{remote-BuZj6AyU.mjs → remote-C8S7FlPF.mjs} +7 -7
  103. package/dist/{result-CR2vk2cj.mjs → result-7F5hEwQJ.mjs} +16 -16
  104. package/dist/{run-BtzE09Dl.mjs → run-BqB0v3pt.mjs} +1 -1
  105. package/dist/{selection-D5cxEl3t.mjs → selection-BNPBZ6T5.mjs} +155 -42
  106. package/dist/{server-bare-CH9X0RLn.mjs → server-bare-A75rDIdD.mjs} +1 -1
  107. package/dist/{server-command-CaBbDNU2.mjs → server-command-CssKFOD7.mjs} +4 -4
  108. package/dist/{server-expo-Ck8EIidM.mjs → server-expo-eQfOdydb.mjs} +2 -2
  109. package/dist/server-expo-wvcj5tiZ.mjs +2 -0
  110. package/dist/{settings-B5tCoq9N.mjs → settings-CosW5X-S.mjs} +1 -1
  111. package/dist/{settings-DzH9mGQZ.mjs → settings-OaGQF-ri.mjs} +1 -1
  112. package/dist/{settings-write-n0zwfohx.mjs → settings-write-QjYfSrjF.mjs} +5 -5
  113. package/dist/settings.schema.json +33 -6
  114. package/dist/{simslim-BjxszUAb.mjs → simslim-Bax1qcxu.mjs} +3 -2
  115. package/dist/{slot-launch-QeuJMlnJ.mjs → slot-launch-CYCEhfBY.mjs} +6 -6
  116. package/dist/{stage-D8xlRU2D.mjs → stage-jKQ7CP_B.mjs} +1 -1
  117. package/dist/{start-C1JLjkNw.mjs → start-C_wefNKB.mjs} +1 -1
  118. package/dist/{start-YMpNwLhh.mjs → start-_BdJ32cZ.mjs} +9 -10
  119. package/dist/{state-DMRNP7-4.mjs → state-C1i2nMOP.mjs} +2 -3
  120. package/dist/{state-DPaM3u5K.mjs → state-C4OdpOTF.mjs} +1 -2
  121. package/dist/{stats-CP7STzZw.mjs → stats-tEpEEH-u.mjs} +4 -4
  122. package/dist/{status-cNw6i0_t.mjs → status-B9M7x4CZ.mjs} +1 -1
  123. package/dist/{status-Fect2w9R.mjs → status-CRXz-Gtj.mjs} +1 -1
  124. package/dist/{status-CRLiJ-O_.mjs → status-DQrqodLj.mjs} +22 -23
  125. package/dist/{status-watch-Bc2J4TZn.mjs → status-watch-B5wDIH_o.mjs} +4 -4
  126. package/dist/status-worker.mjs +1 -1
  127. package/dist/{stim-desktop-DS0mzR8U.mjs → stim-desktop-6Dqp-gjX.mjs} +1 -1
  128. package/dist/{stop-Br7LKxBE.mjs → stop-CmdNhGlC.mjs} +2 -2
  129. package/dist/{stop-Db9Kh-UA.mjs → stop-DRqQVqDJ.mjs} +21 -19
  130. package/dist/{stop-COP6PQqw.mjs → stop-ySNQHXMG.mjs} +4 -5
  131. package/dist/supervisor-run.d.mts +1 -0
  132. package/dist/supervisor-run.mjs +14 -15
  133. package/dist/{support-Cxj9CmII.mjs → support-BGSCBoib.mjs} +4 -4
  134. package/dist/{support-qKcbgmI5.mjs → support-CVOCp0Ft.mjs} +9 -9
  135. package/dist/{swiftpm-artifact-inputs-iY9fsmOc.mjs → swiftpm-artifact-inputs-CfUzt_0Z.mjs} +1 -1
  136. package/dist/{swiftpm-macos-Bp_sn9P4.mjs → swiftpm-macos-x6HPpl_2.mjs} +9 -9
  137. package/dist/toolchain-DoavrvIA.mjs +2 -0
  138. package/dist/toolchain-J4J8MrAr.mjs +2107 -0
  139. package/dist/trigger-DzJu208Q.mjs +2 -0
  140. package/dist/{trigger-CBMe2YLH.mjs → trigger-KVaWWNyn.mjs} +1 -1
  141. package/dist/{warm-progress-BV3ZPmcY.mjs → warm-progress-DvhTX_eu.mjs} +3 -4
  142. package/dist/web-BFLueSh-.mjs +2 -0
  143. package/dist/{web-B4Yjyt1G.mjs → web-Bihbf7-u.mjs} +8 -9
  144. package/dist/web-run.mjs +4 -4
  145. package/dist/{workspace-state-CPSp3mav.mjs → workspace-state-pUg3_RRA.mjs} +45 -7
  146. package/dist/{workspaces-C9w4aeXF.mjs → workspaces-g2lhdBXF.mjs} +17 -17
  147. package/dist/{worktree-CclFIe1Z.mjs → worktree-BKuklFh_.mjs} +34 -31
  148. package/dist/{worktree-Ct9Gk3KL.mjs → worktree-Bm4cb-nU.mjs} +1 -1
  149. package/package.json +8 -8
  150. package/dist/created-devices-D2Aca-xu.mjs +0 -52
  151. package/dist/paths-DQtTgsBf.mjs +0 -42
  152. package/dist/prebuild-JUaqOLpc.mjs +0 -501
  153. package/dist/server-expo-C16Zf2k7.mjs +0 -2
  154. package/dist/toolchain-DB1aeqxO.mjs +0 -2
  155. package/dist/toolchain-VzLJzZ7O.mjs +0 -908
  156. package/dist/trigger-BLsHgAGR.mjs +0 -2
  157. package/dist/web-DS4UDWvf.mjs +0 -2
  158. package/dist/xcode-CiIIDnuD.mjs +0 -910
@@ -1,8 +1,8 @@
1
- import { t as ANDROID_AVD_CONFIG_HELP } from "./settings-B5tCoq9N.mjs";
2
- import { i as findProjectRoot$1 } from "./project-BiC68mIp.mjs";
3
- import { h as RECENT_LAUNCH_MS } from "./eas-session-ledger-BMCA1gdO.mjs";
4
- import { o as guideStatus, t as WATCHMAN_NESTED_WORKTREES } from "./doctor-watchman-BovI1y3-.mjs";
5
- import "./status-watch-Bc2J4TZn.mjs";
1
+ import { t as ANDROID_AVD_CONFIG_HELP } from "./settings-CosW5X-S.mjs";
2
+ import { i as findProjectRoot$1 } from "./project-BUu6Is4k.mjs";
3
+ import { h as RECENT_LAUNCH_MS } from "./eas-session-ledger-B5vygTiD.mjs";
4
+ import { o as guideStatus, t as WATCHMAN_NESTED_WORKTREES } from "./doctor-watchman-BStK05nt.mjs";
5
+ import "./status-watch-B5wDIH_o.mjs";
6
6
  import { SETTINGS, SETTINGS_SCHEMA_URL, TUTORIAL_VERSION } from "@stim-cli/core/state";
7
7
  import chalk from "chalk";
8
8
  //#region src/guide/agent.ts
@@ -188,6 +188,11 @@ RULES DURING THE LOOP
188
188
  you do other work.
189
189
  - Run ios or android again after a native input changes. A JavaScript-only
190
190
  change does not need one.
191
+ - The build cache counts an installed package by its name and version, so it
192
+ cannot see a file you edit inside node_modules/. Change a dependency's native
193
+ code with a patch (patch-package or pnpm patch), which is hashed, or run ios or
194
+ android with --no-build-cache --remote-build local to compile it here. See
195
+ guide lifecycle builds.
191
196
  - For stale Metro transforms or file-map state, use stim start --reset-cache.
192
197
  It restarts only this app's verified owned Metro, preserving devices and other
193
198
  apps' caches. See guide lifecycle for reset scope.
@@ -395,7 +400,7 @@ FULL TOPIC LIST
395
400
  stim guide errors fallbacks # swap, cache, and install notes on a release cache hit
396
401
  stim guide lifecycle # the ordered flow, consent rules, and capacity
397
402
  stim guide lifecycle verification # reproduce, edit, verify the UI, and retain proof
398
- stim guide lifecycle readiness # add optional app readiness logs; no package required
403
+ stim guide lifecycle readiness # add recommended app readiness logs; no package required
399
404
  stim guide lifecycle builds # build optimizations, optional cache warm-up, fingerprints
400
405
  stim guide lifecycle concurrency # shared builds, wait timeouts, capacity limits
401
406
  stim guide lifecycle budget # disk and memory budgets, automatic reclaim
@@ -1009,7 +1014,7 @@ leased until <time>" for each one.`,
1009
1014
  JavaScript running -- the first screen may
1010
1015
  still be rendering\` (\`stable for 3s\` when
1011
1016
  no \`Running\` log arrived). Poll the UI
1012
- before you trust a screenshot. Optional
1017
+ before you trust a screenshot. Recommended
1013
1018
  app-declared readiness adds a separate
1014
1019
  stderr readiness phase; it does not change
1015
1020
  this field. See
@@ -1256,13 +1261,27 @@ leased until <time>" for each one.`,
1256
1261
  pins Node (bin, version and match are null when no install
1257
1262
  fits the pin; match is "exact", or "major" for the newest
1258
1263
  install of the pinned major not older than the pin), else null
1259
- remoteMachines one { machine, state, dnsName?, deviceId?, requestedAt?,
1264
+ remoteMachines one { machine, state, diagnosis?, dnsName?, deviceId?, requestedAt?,
1260
1265
  expiresAt?, offloadable?, reasons?, problems?, capacity?, host? } per
1261
1266
  remote.machines entry, for its build approval; state is "approved", "pending", "not-asked",
1262
1267
  "revoked" (revoked or denied), "lapsed" (the request lapsed
1263
1268
  before approval), "key-changed", "outdated",
1264
1269
  "not-on-tailnet", "tailscale-off", "unreachable" or
1265
- "invalid". host has the same shape and presence rule as in
1270
+ "invalid". diagnosis is { layer, code, title, message, remedy }
1271
+ for the first layer that stopped this machine, present for
1272
+ "tailscale-off", "not-on-tailnet", "outdated" and "unreachable"
1273
+ (deviceHosts carries it too); the codes are
1274
+ tailscale-stopped, tailscale-logged-out, tailscale-unavailable
1275
+ (this machine's Tailscale), peer-not-found, peer-ambiguous,
1276
+ peer-offline (with last seen), peer-no-answer (the connection
1277
+ got no reply and tailscale ping fails), route-missing (nothing accepts
1278
+ connections on the serve port), route-tls, route-http-error
1279
+ (any HTTP status but 502 and 503), server-not-running (502:
1280
+ nothing behind the route), server-not-ready (503: running but
1281
+ not serving; its message gives the cause the server reports),
1282
+ server-no-answer (silent route while tailscale ping reaches the Mac), server-outdated and connect-failed (none of the above).
1283
+ Tailscale's own "offline" flag lags a reachable peer, so an
1284
+ answer from the machine outranks it. host has the same shape and presence rule as in
1266
1285
  deviceHosts, as do expiresAt (a pending request's lapse
1267
1286
  time) and "lapsed" (--fix asks again). An approved machine also carries offloadable,
1268
1287
  true when it would take this app's builds now (iOS
@@ -1278,7 +1297,16 @@ leased until <time>" for each one.`,
1278
1297
  checkout, stim-build, arch, xcode, simulator-sdk, macos-sdk,
1279
1298
  cocoapods, bundler, runtime, jdk, android-sdk, ndk,
1280
1299
  build-tools, compile-sdk, disk, busy or platform). problems lists the same
1281
- reasons as { code, reason } with that code. capacity
1300
+ reasons as { code, reason } with that code; an ndk, build-tools,
1301
+ compile-sdk, jdk, xcode or runtime problem can also carry commands and a
1302
+ lead, the lines to run on that machine (sdkmanager, the JDK install,
1303
+ xcode-select, xcodebuild or xcodes) and what they do, or followsFrom, the
1304
+ code of the problem whose fix also clears it. notes lists what a build there does without stopping:
1305
+ { code, reason, commands, lead } for an ndk, build-tools or compile-sdk package its
1306
+ Android SDK lacks and Gradle downloads on the first build (licenses
1307
+ accepted there, no android.builder.sdkDownload=false in the app), with the sdkmanager
1308
+ command that installs it ahead of time; each
1309
+ also a build-machine-<code> finding at level note. capacity
1282
1310
  is the machine's offer: { running, max, diskFreeBytes,
1283
1311
  minDiskFreeBytes, cpus?, loadPerCore?, builds?, maxBuilds?,
1284
1312
  maxLoadPerCore?, memoryUsedBytes?, memoryTotalBytes?, declined? }; an older stim-server omits the
@@ -1289,6 +1317,7 @@ leased until <time>" for each one.`,
1289
1317
  with an abnormally large footprint and, when it is safe to
1290
1318
  restart, the command
1291
1319
  offload-candidate is a note after 3+ successful local cold builds in 7 days average over 3 min, with no remote.machines and an online tailnet Mac; open Stim Desktop Settings > Remote Machines > Add
1320
+ readiness-markers-missing is a note when no JS or TS source under the app directory contains [stim:readiness]; the scan skips node_modules, native and build output directories and dot-directories, and reports nothing past 5000 source files; see stim guide lifecycle readiness
1292
1321
 
1293
1322
  ON FAILURE
1294
1323
  \`start\`, \`ios\`, \`android\` and \`web\` all print the error contract instead,
@@ -2440,6 +2469,8 @@ RULES
2440
2469
  lastPass carries startedAt, durationMs, trigger, mode, freedBytes
2441
2470
  (0 in report mode), actions, stopped (0), blocked. recent holds the last 20 action, failure
2442
2471
  and blocked NDJSON records from maintenance/maintenance.ndjson.
2472
+ What each run cleared, kept or failed is in $STIM_HOME/gc/gc.ndjson,
2473
+ read with \`stim logs --source gc --json\` (\`guide logs\`, GC LOG).
2443
2474
  invalid names invalid settings; invalid cache caps use their defaults,
2444
2475
  while invalid maintenance settings disable passes. claim contains an
2445
2476
  unresolved claim's path and reason, plus removeCommand from the claim
@@ -3168,6 +3199,7 @@ FLAGS
3168
3199
  shared untagged Metro and app client records (not the web
3169
3200
  page's); default also includes untagged legacy records
3170
3201
  --source <s...> metro, client, device, build, agent, maintenance, placement (one or more), or all.
3202
+ gc alone reads the machine-wide gc log (GC LOG below).
3171
3203
  An unknown value is REJECTED rather than quietly matching
3172
3204
  nothing.
3173
3205
  --level <l> minimum level: debug, info, warn, error, fatal
@@ -3344,6 +3376,27 @@ Add --errors to --source maintenance to show only maintenance_failure events;
3344
3376
  failures before a later launch marker are hidden. Machine passes are reported in
3345
3377
  status and maintenance/maintenance.ndjson; this command reads workspace logs.
3346
3378
 
3379
+ GC LOG
3380
+ stim logs --source gc reads $STIM_HOME/gc/gc.ndjson from any directory: one
3381
+ record per item that a gc run, a maintenance action or the budget gate
3382
+ cleared, kept or failed to clear. It cannot be combined with other sources,
3383
+ and --source all does not include it. --errors shows the failed items.
3384
+ Each record has src: gc, level (info, or error for a failed item), msg and
3385
+ runId the stim invocation, or the pass id of maintenance.ndjson
3386
+ trigger manual (stim gc in a terminal), desktop (STIM_DESKTOP_APP is set, so also a gc run through a stim-server Desktop started),
3387
+ maintenance, or budget (the disk, memory or workspace check before
3388
+ start, ios and android)
3389
+ via maintenance only: the command whose run started the pass
3390
+ options gc only: delete, worktrees, olderThan, cache, idleMs
3391
+ kind what the item was (the kind of gc --json results, plus devServer
3392
+ for a dev server the budget gate stops; maintenance also writes
3393
+ buildDirectory and dependencies)
3394
+ status done, kept or failed
3395
+ label, id, path, bytes, detail
3396
+ name, identifier, absolute path when the item is one, bytes freed,
3397
+ and the reason a kept or failed item was left
3398
+ Records of one run share runId. See stim guide cleanup, WHAT WAS CLEARED.
3399
+
3347
3400
  REMOTE REQUEST RECORDS
3348
3401
  A request to another machine that fails is a warn record with src: build in the
3349
3402
  run's build log: remote_connect_failed { host, capability, ms, timeoutMs, msg,
@@ -3403,6 +3456,8 @@ they stay in the local logs. These records carry no tokens.
3403
3456
 
3404
3457
  WHAT WRITES WHAT
3405
3458
  maintenance.ndjson workspace maintenance actions, failures and explaining skips
3459
+ $STIM_HOME/gc/gc.ndjson machine-wide, not in a workspace: what gc, maintenance
3460
+ and the budget gate cleared (stim logs --source gc)
3406
3461
  metro.ndjson the bundler, in every supervisor mode. Stim's
3407
3462
  middleware adds one record per bundle request
3408
3463
  (bundle_response_started, _progress, _finished,
@@ -3436,7 +3491,7 @@ WHAT WRITES WHAT
3436
3491
  The count does not attribute unknown OS errors to the app.
3437
3492
  Known JavaScript errors also print individually: Android
3438
3493
  ReactNativeJS records, and iOS com.facebook.react.log /
3439
- javascript records. These can interrupt an optional
3494
+ javascript records. These can interrupt a recommended
3440
3495
  readiness wait even without a client or Metro copy.
3441
3496
  Client and Metro errors still print individually.
3442
3497
  A native app that loaded its bundle but reported app errors
@@ -3987,7 +4042,12 @@ too old to speak protocol 2, incompatible
3987
4042
  toolchain/runtime/CPU, low disk, busy, sync/build failure, artifact failure,
3988
4043
  or a checkout that changed while it built. Unsupported build kinds also refuse.
3989
4044
  No local xcodebuild, Gradle or SwiftPM compile or alternate machine follows a
3990
- refusal. Prebuild and pod install still run on this machine before a named offload.
4045
+ refusal. For stim ios, a toolchain difference on the named machine, such as Xcode,
4046
+ the simulator runtime or the CPU, refuses before this machine runs prebuild or pod
4047
+ install, even when prebuild would have produced an app the local cache already holds, or the
4048
+ machine already holds the exact artifact. A
4049
+ missing or different global CocoaPods never refuses a named machine; a pod install
4050
+ that fails on it reports "pod install failed" with the command to run there.
3991
4051
  Unlisted or unpaired names refuse at setup before consulting the cache, without
3992
4052
  a build record or failed-run stats. A listed paired name with a cache hit needs
3993
4053
  no build and does not contact the selected machine.
@@ -5442,10 +5502,11 @@ simulators to volume zero through Xcode's devicectl, and Android 15 and later
5442
5502
  emulators to media volume zero. The iOS mute runs after SimSlim has applied its
5443
5503
  profile and once SpringBoard has finished starting, because both a SimSlim
5444
5504
  reboot and SpringBoard's startup reset the volume. A device that is already
5445
- running keeps its volume, except that SimSlim reboots a running simulator whose
5446
- profile changed. Turn sound on for one device with Sound (iOS) or
5447
- Media sound (Android) in its Stim Desktop options; the choice lasts until that
5448
- device next boots. Android 14 and older images have no shell command that can
5505
+ running keeps its volume, except that a running simulator SimSlim reboots (a
5506
+ first slim, a profile change, or restoring stock services) is muted again once
5507
+ SpringBoard is back; an already slim simulator is not touched. Turn sound on
5508
+ for one device with Sound (iOS) or Media sound (Android) in its Stim Desktop
5509
+ options; the choice lasts until that device next boots. Android 14 and older images have no shell command that can
5449
5510
  set media volume, so startup warns, as it does whenever a mute is not
5450
5511
  confirmed. Host volume, Android alarm and ringtone volumes, hosted devices and
5451
5512
  physical devices are unchanged.
@@ -6320,8 +6381,8 @@ command before any EAS lookup. stim doctor reports it in a project with eas.json
6320
6381
  If another run holds the artifact claim, wait for it to finish and retry.`
6321
6382
  },
6322
6383
  readiness: {
6323
- summary: "implement optional pending/ready app logs, deadlines, errors, and platform isolation",
6324
- body: () => `OPTIONAL APP READINESS
6384
+ summary: "implement recommended pending/ready app logs, deadlines, errors, and platform isolation",
6385
+ body: () => `RECOMMENDED APP READINESS
6325
6386
 
6326
6387
  No package or SDK is needed. This applies to debug ios/android launches with
6327
6388
  Metro verification enabled, not release builds or --no-metro-check.
@@ -6382,7 +6443,10 @@ Only exact standalone info/debug messages captured from this app's device log
6382
6443
  for this platform and current launch are accepted. Stale, other-platform,
6383
6444
  error-level, and embedded example messages are ignored. Unlabelled shared
6384
6445
  Metro output cannot identify a platform. If pending is not captured in time,
6385
- including when device logs are unavailable, the default check applies.
6446
+ including when device logs are unavailable, the default check applies. On a
6447
+ device hosted on another Mac (--remote <machine>), the device log is the copy
6448
+ pulled from that Mac during the launch check, and each window waits up to 10
6449
+ seconds for the copy to cover it.
6386
6450
 
6387
6451
  The app declares readiness; Stim does not inspect a rendered frame. This does
6388
6452
  not change launched JSON semantics or the need to inspect the expected UI and
@@ -6674,7 +6738,8 @@ result as proof instead of requiring an unrelated screenshot.`
6674
6738
 
6675
6739
  \`gc --older-than <days>\` reports and deletes, or with \`--cache parked\`
6676
6740
  erases, only the devices parked at least that long; one parked at an unknown
6677
- time stays.
6741
+ time stays. Automatic maintenance's daily age sweep deletes the devices
6742
+ parked for maintenance.olderThanDays (7; 0 never) the same way.
6678
6743
 
6679
6744
  If simulator listing or deletion fails, \`gc --delete\` reports the failure
6680
6745
  and keeps that entry. It never turns an unverified absence into a dropped
@@ -6846,11 +6911,27 @@ IOS SCHEME SELECTION
6846
6911
 
6847
6912
  AN ARTIFACT THE DEVICE ALREADY HOLDS IS NOT INSTALLED AGAIN
6848
6913
  Both platforms store the artifact verbatim, so its hash is its identity.
6849
- Before installing, Stim hashes the artifact it is about to install and the
6850
- one the device already has -- \`pm path\` then \`sha256sum\` on Android, the
6851
- \`simctl get_app_container\` bundle on iOS. Byte-identical means the install
6852
- is skipped. The phase still reports the cost of proving that identity, but
6853
- avoids the ~43s a 400MB APK can cost to copy and install over USB.
6914
+ Before installing, Stim compares the artifact it is about to install with
6915
+ the one the device already has. On iOS it hashes both bundles: the
6916
+ \`simctl get_app_container\` bundle and the one about to be installed.
6917
+ Byte-identical means the install is skipped. The phase still reports the
6918
+ cost of proving that identity, but avoids the ~43s a 400MB APK can cost to
6919
+ copy and install over USB.
6920
+
6921
+ On Android the proof is one \`adb shell\` call that returns the installed
6922
+ APK's path, size, and the SHA-256 of its tail: the v2 or v3 signing block,
6923
+ the central directory, and the end record. That tail carries the signed
6924
+ digests of every other byte, and Android verified them when it installed
6925
+ the APK. Stim verifies the local APK against the same digests once, with
6926
+ \`apksigner verify\`, and keeps the size and tail digest in
6927
+ \`apk-signed-tail.json\` beside the APK in its build cache entry (stored with
6928
+ the build; an older entry gets it on first use). The same size and tail
6929
+ digest mean the same file, on an owned emulator, an adopted one, a
6930
+ \`--device\` phone and a plain rerun. A different size or tail digest
6931
+ installs without hashing the whole APK. An APK outside a build cache entry,
6932
+ one without a v2 or v3 signing block, an apksigner that is missing or fails,
6933
+ and a device whose \`tail\` or \`stat\` does not answer as expected fall back
6934
+ to \`pm path\` then \`sha256sum\` of the whole installed APK.
6854
6935
 
6855
6936
  install unchanged (emulator-5584 already has this build) (0.4s)
6856
6937
 
@@ -6863,8 +6944,9 @@ AN ARTIFACT THE DEVICE ALREADY HOLDS IS NOT INSTALLED AGAIN
6863
6944
  install unchanged (stim-app already has this build) (0.4s)
6864
6945
  install dev client prepared (0.9s)
6865
6946
 
6866
- The skip needs PROOF. A package that is not installed, a split install, an
6867
- image without \`sha256sum\`, and any adb or simctl failure all read as
6947
+ The skip needs PROOF. A package that is not installed, a split install, a
6948
+ device that answers neither probe (no \`tail\` and no \`sha256sum\`), and any
6949
+ adb or simctl failure all read as
6868
6950
  "cannot determine", and the run installs exactly as it always did. A release
6869
6951
  run swaps this workspace's JS into a COPY of the artifact, which is a
6870
6952
  different artifact and is therefore always installed.
@@ -7065,6 +7147,67 @@ THE BUILD CACHE HAS THREE LEVELS
7065
7147
  trims, and clears local caches only: the provider contract has no delete
7066
7148
  operation, so no local command can remove data a team or CI system shares.
7067
7149
 
7150
+ WHAT THE KEY COUNTS. Stim runs @expo/fingerprint with its default
7151
+ \`balanced\` preset, the one the project's own Expo uses. An installed
7152
+ dependency, and a config plugin loaded from node_modules, counts by its name
7153
+ and version, not by its files. A package whose real path is outside
7154
+ node_modules -- a workspace package, a link: dependency, a local Expo module
7155
+ -- counts by its files, and so do patches/, the app's committed ios/ or
7156
+ android/, the evaluated app config and its local plugins. patches/ and
7157
+ .yarn/patches/ next to a monorepo's lockfile count too, and so do the patch
7158
+ hashes pnpm-lock.yaml records, wherever the patch files live. The app's
7159
+ version, ios.buildNumber and android.versionCode, eas.json, .easignore and
7160
+ the path overrides of react-native.config.js are left out, so a Release
7161
+ cache hit keeps the version it was built with. A project that wants native
7162
+ package files hashed sets \`preset: 'strict'\` in fingerprint.config.js, at
7163
+ about twice the fingerprint's CPU.
7164
+
7165
+ AN EDIT INSIDE node_modules/ IS NOT SEEN. Editing a dependency's native file
7166
+ in place (node_modules/<pkg>/ios, android, cpp, a podspec or build.gradle)
7167
+ leaves the key unchanged, so the next run installs the cached app without
7168
+ the edit. Make the change a patch (patch-package's patches/, or pnpm patch,
7169
+ whose patches the fingerprint hashes), or build it with
7170
+ \`--no-build-cache --remote-build local\`. --no-build-cache alone can still
7171
+ place the build on another machine, which installs dependencies from the
7172
+ lockfile and so builds without the edit. The fresh build is stored under the
7173
+ unchanged key in the machine-wide cache and handed to any cache provider, so
7174
+ every worktree reuses it, edit included, until the dependency's version or
7175
+ patches change; undo the edit with another --no-build-cache build.
7176
+
7177
+ A KNOWN TREE SKIPS THE FULL FINGERPRINT. After a full fingerprint at the
7178
+ first lookup of a React Native or Expo run, Stim records it in
7179
+ $STIM_HOME/fingerprint-memo (~/.stim by default) under a static digest of
7180
+ the tree: git blob ids of the native inputs git tracks, the contents of the
7181
+ untracked, modified and ignored ones, the app config's sources and the files
7182
+ and directories it names, each installed package's path, version and native
7183
+ markers, the lockfile, and the environment variables the config and its
7184
+ .env files read. Any worktree whose tree has the same digest, a fresh one
7185
+ included, reuses the record instead of evaluating the config and running
7186
+ autolinking:
7187
+
7188
+ fingerprint matches a recorded fingerprint of this tree; confirming it in the background
7189
+
7190
+ A full fingerprint then runs at low priority while the app installs and
7191
+ launches. The record is put back only when it agrees. If the run reaches its
7192
+ end first, it waits up to 3 seconds more, then gives the check up and leaves
7193
+ no record, so the next run fingerprints in full. A disagreement prints a
7194
+ warning naming both hashes at the end of the run and writes a
7195
+ fingerprint_memo_stale record to the build log; run the command again to use
7196
+ the current tree.
7197
+
7198
+ A config the digest cannot follow always fingerprints in full: a dynamic
7199
+ require or import, process.env used other than by literal name, file,
7200
+ process or network access while it is evaluated, a dotenv import, a
7201
+ fileHookTransform, or a preset that hashes package files. A local config
7202
+ plugin counts as evaluated too, with one exception: the digest does not
7203
+ track the files a plugin that applies mods reads, assuming it reads them in
7204
+ its mods at prebuild. A plugin that also puts what it reads into the config
7205
+ is a gap only the background check catches. So does a project outside git or on Yarn Plug'n'Play. The record is
7206
+ never consulted after prebuild or pod install, by a build worker, by doctor,
7207
+ or with --no-build-cache; ios --plan and android --plan read one without
7208
+ writing. Records are 0600, at most 256 per platform, and gc prunes them by
7209
+ age with the other caches.
7210
+
7068
7211
  A MISS EXPLAINS ITSELF. Each local cache entry keeps the per-source hashes
7069
7212
  of its fingerprint (hashes only, pruned with the entry). Before compiling,
7070
7213
  Stim compares the sources it is about to store with a baseline: the entry
@@ -7397,9 +7540,13 @@ OPT-IN CONCURRENCY LIMITS (UNLIMITED BY DEFAULT)
7397
7540
  status\` shows "shut down after 30m idle" on the device until the next
7398
7541
  \`ios\` or \`android\` run boots it again. When metro.idleStopMinutes is
7399
7542
  shorter, the dev server's idle stop first shuts down the devices idle that
7400
- long, because no supervisor is left to check afterwards. The supervisor
7401
- path does not check without a supervisor: release runs, and after
7402
- \`stim stop\`. The setting is read when the supervisor starts. Stim Desktop's simulator view is not a
7543
+ long, because no supervisor is left to check afterwards. Devices with no
7544
+ supervisor (release runs, a dev server that exited) are shut down by
7545
+ automatic maintenance while maintenance.mode is on (guide cleanup): every maintenance.idleCheckMinutes
7546
+ (5) a command starts a background check that applies each workspace's
7547
+ current setting with the same idle rules, native-run lock recheck, status
7548
+ and log record, and skips the workspace of the command that started it.
7549
+ The supervisor reads the setting when it starts. Stim Desktop's simulator view is not a
7403
7550
  stim-server client and does not count as a viewer. Change it with
7404
7551
  \`stim settings set devices.idleShutdownMinutes <minutes> --scope machine\`;
7405
7552
  0 turns it off, and an explicit 0 stays 0.
@@ -8160,17 +8307,23 @@ THE POOL: WHICH DEVICE AN ID-LESS \`--device\` PICKS
8160
8307
  \`ios.simslim off\` opts that app out and restores stock services on its
8161
8308
  simulator. The defaults disable push, StoreKit, iCloud, Safari sync, Mail,
8162
8309
  Calendar and Contacts services, among others; move an app that needs them
8163
- to a profile that keeps those categories, or to \`off\`. A running or
8164
- parked simulator is slimmed the next time Stim boots or adopts it, and an
8165
- already slim simulator is left alone, so only the first slim reboots it.
8310
+ to a profile that keeps those categories, or to \`off\`. A running
8311
+ simulator is slimmed on the next \`stim ios\`, without a boot first, and a
8312
+ parked simulator when Stim adopts it. An already slim simulator is left
8313
+ alone, so only the first slim reboots it.
8166
8314
  Without the \`simslim\` command, doctor reports it and \`stim ios\` boots
8167
8315
  the simulator unslimmed with a one-line note. Hosted simulators on a named
8168
8316
  Mac are never slimmed, whatever either Mac's setting says.
8169
8317
 
8170
8318
  SimSlim 0.8 requires iOS 18.5 or newer. On each local \`stim ios\`,
8171
8319
  Stim reconciles that profile on the owned simulator before the app build.
8172
- The first change can update services and reboot the simulator. A matching
8173
- profile is a fast no-op on later launches. The settings persist across normal
8320
+ The first change can update services and reboot the simulator. Stim records
8321
+ the applied profile (the default categories, or a hash of the profile file)
8322
+ and the boot of the simulator it was applied on, so a later launch on the
8323
+ same boot with the same profile skips the \`simslim\` call. A changed
8324
+ profile, a reboot, or a missing record reconciles again. A manual
8325
+ \`simslim off\` on a running simulator is not noticed until the simulator
8326
+ reboots or the profile changes. The settings persist across normal
8174
8327
  shutdowns and reboots. Removing the setting restores stock services when
8175
8328
  Stim applied the profile. Stim never changes an unowned or remote simulator.
8176
8329
  Each SimSlim operation has a 12-minute outer deadline, including discovery.
@@ -8267,13 +8420,14 @@ and logs, and never deletes anything. off disables it. It is off in CI and
8267
8420
  scoped STIM_HOME homes unless STIM_MAINTENANCE is explicit, so a scratch home
8268
8421
  or CI run never deletes on its own.
8269
8422
  Commands trigger a detached pass when a check is due: disk and memory pressure
8270
- (every minute), directory sizes (hourly), finished worktrees (every 15
8271
- minutes) and the age sweep (daily). guide, settings, help and every gc
8423
+ (every minute), idle devices (every 5 minutes, maintenance.idleCheckMinutes),
8424
+ directory sizes (hourly), finished worktrees (every 15 minutes) and the age
8425
+ sweep (daily). guide, settings, help and every gc
8272
8426
  invocation do not trigger it; gc without --delete prints the maintenance plan
8273
8427
  itself.
8274
8428
  status --watch and every running dev server supervisor also trigger checks
8275
8429
  each minute, so the disk check runs while no command does.
8276
- Size, worktree and sweep checks defer under high load. Attempts back off for at
8430
+ Size, idle, worktree and sweep checks defer under high load. Attempts back off for at
8277
8431
  least one minute. Measured directories are workspace build outputs and Stim's
8278
8432
  shared native, Metro, ccache, Swift compilation and registered caches. The size
8279
8433
  check also records the disk of each owned and parked simulator and emulator
@@ -8282,7 +8436,10 @@ Pressure checks read free disk on the Stim home, projects and worker root
8282
8436
  volumes. On macOS the memory signal is the sysctl pressure level, with no
8283
8437
  signal if sysctl fails. Other platforms use os.freemem(); macOS never falls
8284
8438
  back to it. Memory pressure is recorded only; stopping devices, dev servers
8285
- and helpers is not automatic. While the level is warning or critical, the
8439
+ and helpers for memory is not automatic. The idle check shuts down (never
8440
+ deletes) owned simulators and emulators idle for their workspace's
8441
+ devices.idleShutdownMinutes, also with no supervisor running, through the
8442
+ supervisor's idle rules and records (guide lifecycle budget). While the level is warning or critical, the
8286
8443
  check also names abnormally large processes (guide lifecycle simslim) in
8287
8444
  status's maintenance block and the maintenance log.
8288
8445
 
@@ -8299,13 +8456,25 @@ In on mode a pass runs, cheapest to rebuild first:
8299
8456
  claim (taken for each xcodebuild run that compiles with it) or that
8300
8457
  claim is unresolved; the emptying holds the exclusive claim
8301
8458
  5. linked worktrees whose branch or pull request finished (gc's finished
8302
- worktree rules and gc.worktreeGraceMinutes; maintenance.removeFinishedWorktrees)
8303
- 6. owned simulators and emulators of a finished worktree that is kept, such
8459
+ worktree rules and gc.worktreeGraceMinutes; maintenance.removeFinishedWorktrees),
8460
+ and, with maintenance.removeIdleWorktrees (off by default), in the age
8461
+ sweep clean, pushed ones unused for olderThanDays, as gc --delete
8462
+ --worktrees --older-than removes them, git-ignored files included; never
8463
+ a dirty one, one with unpushed commits, the source checkout, or one whose
8464
+ last use is unknown
8465
+ 6. parked simulators and emulators unused for olderThanDays in the age
8466
+ sweep, through centralized teardown with ownership checked again
8467
+ 7. owned simulators and emulators of a finished worktree that is kept, such
8304
8468
  as a dirty one (not one locked with git worktree lock), once it finished
8305
8469
  and was idle for gc.worktreeGraceMinutes (at least protectRecentHours),
8306
8470
  and of any
8307
8471
  workspace idle for maintenance.deviceIdleDays (3; 0 turns it off) in the
8308
8472
  age sweep. Devices are recreated on the next run.
8473
+ The age sweep runs every maintenance.sweepHours (24) in the background of
8474
+ commands, never at a set hour.
8475
+ maintenance.olderThanDays (7) is its age for steps 2, 3, 5 and 6; with 0 the
8476
+ sweep removes nothing in them by age, while step 1, step 7, finished
8477
+ worktrees and the cap and disk-driven parts of steps 2 and 3 still run.
8309
8478
  Disk-driven steps stop once free space is back above the floor. Kept for
8310
8479
  every pass: a workspace that is in use, holds a lock, was used within
8311
8480
  maintenance.protectRecentHours, is pinned with maintenance.keep, or is the
@@ -8401,6 +8570,23 @@ claim fails with ENOSPC or EDQUOT, the pass deletes it and retries once, so a
8401
8570
  pass starts at zero free disk as long as an earlier pass left the reserve; the
8402
8571
  pass writes it again at its end.
8403
8572
 
8573
+ WHAT WAS CLEARED
8574
+ Every run that deletes or changes something appends one record per item to
8575
+ the machine gc log, $STIM_HOME/gc/gc.ndjson: gc --delete with any flags, gc
8576
+ --idle, gc --cache --delete, each maintenance action taken, and what the budget gate
8577
+ reclaims before stim start, ios or android. A dry run writes nothing, and a
8578
+ gc run that only kept items writes nothing. Failed and kept items carry their
8579
+ reason. The file rotates at maintenance.logMaxMb and its old generation is
8580
+ kept for maintenance.logRetentionDays, like the maintenance log.
8581
+ stim logs --source gc the retained records, oldest first
8582
+ stim logs --source gc --json one record per line
8583
+ stim logs --source gc --errors only the items that failed
8584
+ gc works from any directory because the log is machine-wide; it cannot be
8585
+ combined with other --source values and --source all leaves it out. Fields
8586
+ (\`guide logs\`, GC LOG): runId, trigger (manual, desktop, maintenance or
8587
+ budget), options, kind, status (done, kept or failed), label, id, path,
8588
+ bytes, detail.
8589
+
8404
8590
  CLEANUP AND DISK
8405
8591
 
8406
8592
  worktree remove stops the agent-device daemon when the workspace's
@@ -8805,7 +8991,9 @@ SWEEPING FINISHED WORKTREES
8805
8991
  one, else by the RECENT ACTIVITY sources above being older than
8806
8992
  --older-than days (reason recently-used while they are newer, and
8807
8993
  last-use-unknown when they cannot be read). Automatic maintenance removes it
8808
- only at disk pressure level 3, once that activity is older than
8994
+ the same way in its daily age sweep, after maintenance.olderThanDays, only
8995
+ with maintenance.removeIdleWorktrees (off by default), and at disk pressure
8996
+ level 3 once that activity is older than
8809
8997
  maintenance.pressureProtectMinutes and at least 5 minutes old, whatever
8810
8998
  the setting; unreadable activity keeps it. Removal re-checks, under the removal
8811
8999
  locks, that HEAD did not move and the worktree is still idle, and
@@ -9495,9 +9683,9 @@ KEYS STIM READS
9495
9683
  iOS work after reviewing service tradeoffs; see
9496
9684
  \`stim guide lifecycle simslim\`. Each local \`stim ios\`
9497
9685
  reconciles the profile on its Stim-owned simulator.
9498
- The first change can reboot it; a matching profile is a
9499
- fast no-op. Removing the setting restores stock services
9500
- when Stim applied the profile. Absolute paths, root or
9686
+ The first change can reboot it; an unchanged profile on
9687
+ the same boot is skipped. Removing the setting
9688
+ restores stock services when Stim applied the profile. Absolute paths, root or
9501
9689
  symlink escapes, and missing files are refused before
9502
9690
  simulator creation. Remote and unowned simulators are
9503
9691
  never changed.
@@ -10088,6 +10276,11 @@ never decide who the worker is. With --fix, a pending entry is replaced when
10088
10276
  STIM_ACCESS_TICKET names another ticket.
10089
10277
  Approved doctor entries under remoteMachines and deviceHosts include host
10090
10278
  { name, screenRecording, accessibility } when the worker reports it.
10279
+ A remote machine doctor cannot use carries a diagnosis { layer, code, title,
10280
+ message, remedy } naming the first layer that failed: Tailscale on this machine,
10281
+ the peer, the serve route, stim-server, or compatibility. Tell the user its
10282
+ message and remedy; the remedies run on the remote machine or in Tailscale, so
10283
+ agents do not run them. \`stim guide facts doctor\` lists the codes.
10091
10284
 
10092
10285
  REMOTE MACHINES AND PHONE APP
10093
10286
  When the user mentions slow builds, a busy machine, a full disk or device limit, or
@@ -10263,7 +10456,8 @@ at setup before consulting the cache; they create no build record or failed-run
10263
10456
  stats. A listed paired name with a cache hit compiles nothing, contacts no worker
10264
10457
  and records buildMachine with builtOn absent. Build failures retain the selected
10265
10458
  value; builtOn is here or the configured machine entry when compilation ran.
10266
- Prebuild and pod install still run on this machine before a named offload. A refusal
10459
+ Prebuild and pod install still run on this machine before a named offload; stim ios
10460
+ checks the machine's toolchain first. A refusal
10267
10461
  starts no local xcodebuild, Gradle or SwiftPM compile.
10268
10462
  A React Native offload sends the tracked and untracked files, and the gitignored
10269
10463
  files the native fingerprint hashes or the Expo config reads, such as .env.local
@@ -10360,18 +10554,44 @@ match. Completed archives survive worker checkout changes or removal; hits
10360
10554
  need no source upload or build slot. Disabled remote artifact reads skip this
10361
10555
  lookup. Release and physical-device builds keep their existing paths.
10362
10556
  SwiftPM, compiler and dependency caches use their existing paths.
10363
- For iOS the machine's Xcode and simulator SDK must match, and it needs an
10364
- iPhone simulator on the target runtime. Its project-selected CocoaPods must match too, unless
10365
- the app's Gemfile.lock, in ios/ or the project root, pins CocoaPods: both machines then run that version through
10366
- bundler, so the machine needs only Bundler on its stim-server PATH and
10557
+ For iOS the machine's Xcode and simulator SDK must match. Its offer reports the
10558
+ developerDir xcode-select prints and the Xcode apps in /Applications (version,
10559
+ build, and license and first-launch checks while no Xcode works), so doctor's
10560
+ fix for no usable Xcode is the exact command to run there: sudo xcode-select -s
10561
+ <the Xcode with this machine's build, else the newest>/Contents/Developer when
10562
+ the Command Line Tools or another Xcode is selected, sudo xcodebuild -license
10563
+ accept and sudo xcodebuild -runFirstLaunch for the checks that fail, or xcodes
10564
+ install <version> with no Xcode app. A runtime the machine lacks gets
10565
+ xcodebuild -downloadPlatform iOS -buildVersion <version>, or a pointer to the
10566
+ Xcode problem when xcode-select is the cause. A stim-server that reports no
10567
+ developerDir keeps the prose fix. It also needs an
10568
+ iPhone simulator on the target runtime. A missing or different global CocoaPods
10569
+ keeps automatic placement from choosing the machine for a project that does not
10570
+ pin it, unless the machine already holds the exact artifact; the placement line
10571
+ says why and doctor shows the install command. A named --remote-build still
10572
+ sends the build, and a pod install that fails there reports "pod install failed"
10573
+ with the fix on that machine. When the app's Gemfile.lock, in ios/ or the project
10574
+ root, pins CocoaPods, both machines run that version through
10575
+ bundler, so the machine needs Bundler on its stim-server PATH and
10367
10576
  installs the pinned gems itself on the first build. This Mac's side of the
10368
10577
  comparison runs \`pod --version\` in the directory pod install runs in. The comparison selects
10369
10578
  the app's .ruby-version when installed, otherwise rvm's or rbenv's default Ruby, and only when neither exists the GEM_HOME, GEM_PATH and Ruby-related PATH entries of the machine's login shell (read once with $SHELL -lic), unless the caller already sets GEM_HOME, with pod install's UTF-8 locale defaults. pod install, including the Bundler run, uses the same environment.
10370
10579
  For Android its CPU may differ, since the APK's ABIs come from the NDK, so an
10371
10580
  x86_64 Linux or Windows machine running stim-server can build for an Apple silicon Mac. Its JDK major
10372
- version must match, and its Android SDK must hold the NDK, build-tools and
10581
+ version must match this machine's, which can be an installed JDK that is not the
10582
+ worker's default: its offer lists \`jdks\`, and a build sent with the build-jdk
10583
+ feature runs with JAVA_HOME at the one of that major. A major no installed JDK
10584
+ has gets the install command for the worker's OS as doctor's fix. A nearby major
10585
+ never counts (Gradle, AGP and Kotlin support and the shared build cache assume
10586
+ one JDK). Also, its Android SDK must hold the NDK, build-tools and
10373
10587
  compile platform that the project's React Native version names in
10374
- gradle/libs.versions.toml; Gradle and AGP come from the synced project. When
10588
+ gradle/libs.versions.toml; Gradle and AGP come from the synced project. A
10589
+ package the SDK lacks is a note, not a blocker, when its offer reports the SDK
10590
+ licenses accepted (\`licenses/android-sdk-license\`) and the app's
10591
+ Gradle root's gradle.properties does not set android.builder.sdkDownload=false:
10592
+ Gradle downloads the package on the first build there (the NDK is about 1-2 GB).
10593
+ Otherwise it blocks, and doctor's fix is the sdkmanager commands to run on that
10594
+ machine; a stim-server that does not report its licenses keeps that rule. When
10375
10595
  no machine takes the build in auto placement, the run prints one line with each machine's
10376
10596
  reason, such as \`janics-mac-mini: busy (load at or above 2/core; load
10377
10597
  8.2/core, 2 builds) -> building here\`, and compiles here. It sends the files
@@ -1,7 +1,7 @@
1
1
  import { c as throwIfCancelled } from "./exec-VFRpu1iA.mjs";
2
- import { c as call, l as connectHost } from "./hosted-client-rsRqysJc.mjs";
3
- import { i as requestHostedMetro } from "./metro-gateway-Bp9gJQAN.mjs";
4
- import { a as stopHostedNative, i as prepareHostedNative, r as placeHostedNative } from "./hosted-native-BxTizGU2.mjs";
2
+ import { c as call, l as connectHost } from "./hosted-client-fHO8yqzc.mjs";
3
+ import { i as requestHostedMetro } from "./metro-gateway-B6_PgD0L.mjs";
4
+ import { a as stopHostedNative, i as prepareHostedNative, r as placeHostedNative } from "./hosted-native-CpDB3-PO.mjs";
5
5
  //#region src/device-host/hosted-android.ts
6
6
  async function prepareHostedAndroid(machine, selectors, recorded, mode) {
7
7
  return await prepareHostedNative(machine, selectors, recorded, "android", false, mode);
@@ -1,7 +1,7 @@
1
1
  import { c as throwIfCancelled, o as cancellableSleep } from "./exec-VFRpu1iA.mjs";
2
- import { rt as resolveEndpoint } from "./config-BZhXY15H.mjs";
3
- import { t as BuildConnection } from "./client-BQsmyp_-.mjs";
4
- import { d as configuredMachines } from "./selection-D5cxEl3t.mjs";
2
+ import { at as resolveEndpoint } from "./config-BktkY_zC.mjs";
3
+ import { t as BuildConnection } from "./client-D9CaRks-.mjs";
4
+ import { m as configuredMachines } from "./selection-BNPBZ6T5.mjs";
5
5
  import { createHash } from "node:crypto";
6
6
  import { lstatSync, readFileSync, readdirSync, readlinkSync } from "node:fs";
7
7
  import { join } from "node:path";
@@ -50,11 +50,11 @@ async function connectHost(machine, timeoutMs = CONNECT_TIMEOUT_MS, strict = fal
50
50
  signal?.throwIfAborted();
51
51
  if (typeof target === "string") throw Object.assign(/* @__PURE__ */ new Error(`Stim does not connect to ${machine}: ${target}.`), { code: "STIM_HOSTING_REFUSED" });
52
52
  const opened = await BuildConnection.open(target, credential.serverKey, timeoutMs, "device-host", signal).catch((error) => {
53
- throw Object.assign(new Error(`${machine} is unreachable. Check stim-server and its tailnet serve route on ${machine}.`, { cause: error }), { code: strict ? "STIM_HOSTING_REFUSED" : "closed" });
53
+ throw Object.assign(new Error(`${machine} is unreachable. Run stim doctor to see which layer failed.`, { cause: error }), { code: strict ? "STIM_HOSTING_REFUSED" : "closed" });
54
54
  });
55
55
  if (signal?.aborted && opened instanceof BuildConnection) opened.close();
56
56
  signal?.throwIfAborted();
57
- if (!(opened instanceof BuildConnection)) throw Object.assign(/* @__PURE__ */ new Error(opened.code === "server-outdated" ? `${machine} runs a stim-server that speaks an older protocol than this Stim. Run stim doctor for the command that updates it there, then retry.` : opened.refused ? `${machine} refused this machine: ${opened.failure.replace(/\.+$/, "")}. Run stim doctor.` : `${machine} is unreachable: ${opened.failure}. Check stim-server and its tailnet serve route on ${machine}.`), {
57
+ if (!(opened instanceof BuildConnection)) throw Object.assign(/* @__PURE__ */ new Error(opened.code === "server-outdated" ? `${machine} runs a stim-server that speaks an older protocol than this Stim. Run stim doctor for the command that updates it there, then retry.` : opened.refused ? `${machine} refused this machine: ${opened.failure.replace(/\.+$/, "")}. Run stim doctor.` : `${machine} is unreachable: ${opened.failure}. Run stim doctor to see which layer failed.`), {
58
58
  code: strict ? "STIM_HOSTING_REFUSED" : opened.refused ? "STIM_HOSTING_REFUSED" : opened.code ?? "closed",
59
59
  hostCode: opened.code
60
60
  });
@@ -1,4 +1,4 @@
1
- import { a as stopHostedNative, i as prepareHostedNative, r as placeHostedNative } from "./hosted-native-BxTizGU2.mjs";
1
+ import { a as stopHostedNative, i as prepareHostedNative, r as placeHostedNative } from "./hosted-native-CpDB3-PO.mjs";
2
2
  //#region src/device-host/hosted-ios.ts
3
3
  async function prepareHostedIos(machine, selectors, recorded, mode) {
4
4
  return await prepareHostedNative(machine, selectors, recorded, "ios", false, mode);