stim 1.3.0 → 1.4.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 (65) hide show
  1. package/README.md +17 -3
  2. package/dist/{android-CqcW1hrP.mjs → android-BTR1KTMP.mjs} +206 -120
  3. package/dist/{android-BqVIJoG7.mjs → android-BkM4qTaN.mjs} +76 -77
  4. package/dist/{android-cas-BR5YJBYv.mjs → android-cas-BjLRZyks.mjs} +15 -12
  5. package/dist/{app-install-DIp9riNx.mjs → app-install--PAocWvq.mjs} +2 -2
  6. package/dist/{build-slots-C3-R4XQr.mjs → build-slots-D6sDzOe-.mjs} +7 -8
  7. package/dist/{cache-manifest-CXR-Y8pH.mjs → cache-manifest-D02TpYjA.mjs} +1 -1
  8. package/dist/cache-manifest.mjs +1 -1
  9. package/dist/cli.mjs +14 -13
  10. package/dist/collector-run.mjs +4 -4
  11. package/dist/{command-output-8wHMEde0.mjs → command-output-BuIb_ZOI.mjs} +1 -4
  12. package/dist/{config-CcefMY2Q.mjs → config-6vdGngKa.mjs} +51 -51
  13. package/dist/dependency-state-BMirHEPD.mjs +76 -0
  14. package/dist/{deps-Spz8ozgo.mjs → deps-Ck4wuTwa.mjs} +1 -1
  15. package/dist/{dev-client-BjisHDB5.mjs → dev-client-Cd0v7iZk.mjs} +12 -12
  16. package/dist/{device-BkiW-gkk.mjs → device-CKXLnRRb.mjs} +7 -7
  17. package/dist/device-CMDFzx7C.mjs +1136 -0
  18. package/dist/{device-lease-BJYybF-2.mjs → device-lease-DDJr85OK.mjs} +3 -3
  19. package/dist/{device-pool-CPbLGfrB.mjs → device-pool-DJvbIBGW.mjs} +4 -4
  20. package/dist/{device-remote-AkYCKS8U.mjs → device-remote-CMfHhoc8.mjs} +7 -6
  21. package/dist/{doctor-Bmv86UUW.mjs → doctor-B_mfURdt.mjs} +536 -114
  22. package/dist/{error-diagnostics-DHC31rXA.mjs → error-diagnostics-_VmkqRZo.mjs} +8 -7
  23. package/dist/{gc-GGu-8ULN.mjs → gc-0AyxmECv.mjs} +61 -27
  24. package/dist/{guide-92CPA21s.mjs → guide-CGxdmQ0K.mjs} +236 -67
  25. package/dist/{ios-BSXFeO5H.mjs → ios-CtmGLx3X.mjs} +2667 -2607
  26. package/dist/{ios-device-CWOM_S44.mjs → ios-device-DJOi9FR5.mjs} +1 -1
  27. package/dist/{logs-DPNF4C9s.mjs → logs-BF3qPA7w.mjs} +32 -7
  28. package/dist/{logs-query-R-Cqc6Qv.mjs → logs-query-Cdp5aaCH.mjs} +1 -1
  29. package/dist/metro-DFLmaVz8.mjs +99 -0
  30. package/dist/named-ports-BO7Zpjp0.mjs +137 -0
  31. package/dist/{ownership-8O073GIC.mjs → ownership-BvSpZVBk.mjs} +5 -5
  32. package/dist/{ownership-BpCKiRlQ.mjs → ownership-C08Yzrp7.mjs} +6 -6
  33. package/dist/ports-BiKqP92J.mjs +39 -0
  34. package/dist/ports-ClqG8GPt.mjs +90 -0
  35. package/dist/{project-fKmuIaMI.mjs → project-CBFCwnN5.mjs} +2 -2
  36. package/dist/{reclaim-iW9Cad7k.mjs → reclaim-DpDVo-AP.mjs} +20 -13
  37. package/dist/{reload-DazHDSFA.mjs → reload-ByTqg6ZI.mjs} +8 -8
  38. package/dist/rolldown-runtime-BsOwBuO_.mjs +30 -0
  39. package/dist/{server-bare-DEzPZLvf.mjs → server-bare-Buk-k8tI.mjs} +3 -6
  40. package/dist/{server-expo-DizT4OMk.mjs → server-expo-D4P7iTvf.mjs} +3 -3
  41. package/dist/server-expo-Dho5Raiw.mjs +2 -0
  42. package/dist/{settings-DEJf6_kJ.mjs → settings-DwfltIXQ.mjs} +2 -2
  43. package/dist/{slot-launch-cci0xvmC.mjs → slot-launch-DeKen9e5.mjs} +4 -4
  44. package/dist/{start-mObjQH2W.mjs → start-BpKUHcU_.mjs} +16 -14
  45. package/dist/state-CDbvmZdt.mjs +131 -0
  46. package/dist/{stats-BR-kwj0B.mjs → stats-C59QLKm2.mjs} +1 -1
  47. package/dist/{stats-CaaQr4yN.mjs → stats-k5qMjyxo.mjs} +4 -4
  48. package/dist/{status-BxQNqu9t.mjs → status-Y2ij3dm6.mjs} +13 -12
  49. package/dist/{stop-DimGXEKY.mjs → stop-_P2OQwmZ.mjs} +15 -14
  50. package/dist/{stop-CrHGcjrV.mjs → stop-vKmmxhea.mjs} +2 -2
  51. package/dist/supervisor-run.d.mts +3 -27
  52. package/dist/supervisor-run.mjs +7 -6
  53. package/dist/{teardown-ClFp1Aax.mjs → teardown-KsPpvldq.mjs} +248 -110
  54. package/dist/{workspace-process-lock-3NcQnZ0X.mjs → workspace-process-lock-UEIrvZMJ.mjs} +4 -4
  55. package/dist/workspace-state-Do1e2mOH.mjs +146 -0
  56. package/dist/{worktree-DxTgN1CJ.mjs → worktree-BvcAIMdJ.mjs} +132 -123
  57. package/dist/{worktree-Eq6TLH8N.mjs → worktree-DWNx4tUg.mjs} +32 -2
  58. package/dist/xcode-D9tVItl-.mjs +860 -0
  59. package/package.json +7 -7
  60. package/dist/doctor-fAMXC4vl.mjs +0 -530
  61. package/dist/metro-CORqXyTL.mjs +0 -184
  62. package/dist/ownership-claim-Bf2CAfw3.mjs +0 -570
  63. package/dist/server-expo-44em5Pmg.mjs +0 -2
  64. package/dist/state-B3XA2wp2.mjs +0 -268
  65. package/dist/xcode-D7sAp-Zy.mjs +0 -1858
@@ -1,4 +1,4 @@
1
- import { t as ANDROID_AVD_CONFIG_HELP } from "./settings-DEJf6_kJ.mjs";
1
+ import { t as ANDROID_AVD_CONFIG_HELP } from "./settings-DwfltIXQ.mjs";
2
2
  import chalk from "chalk";
3
3
  //#endregion
4
4
  //#region src/guide/index.ts
@@ -62,6 +62,8 @@ warm checks for existing entries before copying, not during the copy. Do not
62
62
  edit files, install dependencies, or run another warm in that worktree until
63
63
  it finishes; concurrent files can be overwritten or removed.
64
64
  If warm fails or reports incomplete, resolve the reported failure first.
65
+ A warm that cannot record its ownership claim refuses before copying. Follow
66
+ the printed remedy and keep concurrent runs on the same STIM_HOME.
65
67
 
66
68
  Before native worktree work, run doctor for the platform in scope. It checks
67
69
  the source checkout from a linked worktree. Fix relevant findings and inspect the
@@ -80,6 +82,12 @@ current and highest observed pressure; a timeout does not establish OOM.
80
82
  Avoid repeated reboots under unchanged pressure; do not restart other
81
83
  workspaces' devices or close their apps without asking. That guide covers
82
84
  the recovery steps and profile tradeoffs.
85
+ An Android boot timeout under observed elevated memory pressure gets one
86
+ extra wait while the emulator process this run started is alive. It never
87
+ launches a second emulator. For a timeout, read guide errors STIM_NO_DEVICE
88
+ and free memory by stopping only unneeded devices in workspaces you own;
89
+ ask before closing other apps or devices. Device count alone is not evidence
90
+ of memory pressure.
83
91
  For a linked native library carrying Git metadata, add the printed .git entries to
84
92
  .fingerprintignore only when the native build does not read Git state.
85
93
 
@@ -180,8 +188,9 @@ RULES DURING THE LOOP
180
188
  - A clean logs --errors check requires exit code 0 AND no matching errors in
181
189
  captured logs. Exit code 0 alone means the query succeeded, even when errors
182
190
  were printed. Human output shows "No matching log records" on stderr for
183
- zero matches; JSON mode prints zero bytes. This does not prove launch or log
184
- capture succeeded. Do not read the NDJSON files directly.
191
+ zero matches; JSON mode prints zero bytes. A workspace with no captured
192
+ timeline refuses with STIM_NO_PROJECT and, in a monorepo, names the nearest
193
+ registered descendant app with logs. Do not read the NDJSON files directly.
185
194
  - Use stim status when resuming a workspace or recovering missing device,
186
195
  port, server, or build facts. A normal start and platform run already print
187
196
  them. Use stim doctor when a build is unexpectedly slow or the environment
@@ -196,6 +205,8 @@ the stim-<label> (<model> <runtime>) name. Never point Stim at a user-created
196
205
  emulator or simulator.
197
206
 
198
207
  worktree remove parks the workspace's simulator or emulator for later adoption.
208
+ Before owned-device teardown, Stim best-effort closes local agent-device sessions
209
+ on that exact device; failures warn and teardown continues (guide cleanup).
199
210
  A parked device is Stim-owned: never delete one by hand. gc --delete clears verified
200
211
  entries and keeps failures; see guide lifecycle pool. First launch on a
201
212
  physical iPhone can need the one-time taps named by the remedy.
@@ -215,8 +226,10 @@ shut down the phone or uninstall the app.
215
226
 
216
227
  Treat a refusal as an ownership or state mismatch: read its code and remedy.
217
228
  Never reach for --force first.
218
- Stim leaves externally started servers alone. Stop them with their original
219
- tool; neither a matching port nor --force grants process ownership.
229
+ Stim leaves externally started Metro servers alone. Stop them with their original
230
+ tool; neither a matching Metro port nor --force grants process ownership.
231
+ Named ports are separate: ports stop kills TCP listeners on the workspace's
232
+ reserved named ports, even outside the workspace. Read guide ports before use.
220
233
 
221
234
  Ask the user before these actions:
222
235
 
@@ -255,6 +268,7 @@ Read the matching guide before acting in these situations:
255
268
  | Refusal with a CODE | stim guide errors <CODE> |
256
269
  | Running under a sandbox | stim guide errors sandbox |
257
270
  | Release configuration or ...Release variant | stim guide lifecycle release |
271
+ | Web or API server ports | stim guide ports |
258
272
  | Remote device, custom Metro, or tunnel | stim guide metro |
259
273
  | Cache miss, bypass, or fingerprint exclusions | stim guide lifecycle builds |
260
274
  | Capacity limits | stim guide lifecycle concurrency |
@@ -290,6 +304,7 @@ FULL TOPIC LIST
290
304
  stim guide lifecycle release # Release configurations and ...Release variants
291
305
  stim guide facts # the --json payloads
292
306
  stim guide facts devmenu # the Expo dev menu or Tools button over the app
307
+ stim guide ports # named ports for web and API servers
293
308
  stim guide metro # supervisor, custom Metro, tunnels, and remote devices
294
309
  stim guide logs # filters, record shape, and capture limits
295
310
  stim guide cleanup # what reclaims a device, and what deletes
@@ -885,6 +900,58 @@ STARTING YOUR OWN BUNDLER STILL WORKS
885
900
 
886
901
  \`stop\` leaves an externally started server running. Stop it with the tool
887
902
  that started it.`
903
+ },
904
+ ports: {
905
+ summary: "Workspace ports for web and API servers that Stim does not manage",
906
+ body: () => `NAMED SERVER PORTS
907
+
908
+ If Stim is not installed globally, replace stim with npx stim.
909
+
910
+ stim ports get web
911
+ pnpm exec vite --port "$(stim ports get web)" --strictPort
912
+ stim ports
913
+ stim ports stop web --dry-run
914
+ stim ports stop web
915
+ stim ports release api
916
+
917
+ get <label> allocates on first use and prints only the number to stdout.
918
+ Repeated calls reuse it, even while a server is listening. Labels start with
919
+ a letter and contain up to 64 letters, digits, underscores, or hyphens.
920
+ metro is reserved; use stim start and stim stop for managed Metro.
921
+
922
+ New allocations scan TCP ports 8900-8999. They skip registry reservations
923
+ and existing listeners, announcing occupied ports and upward retries on
924
+ stderr. Allocation requires lsof. All 100 ports occupied or reserved is a
925
+ refusal; stop or release unused allocations in their owning workspaces.
926
+
927
+ The machine registry, under STIM_HOME, serializes allocation and cleanup.
928
+ The workspace is the nearest package.json directory, resolved through
929
+ symlinks. Use the same package directory for every command in a monorepo.
930
+ The allocation reserves a number, not a listening socket. Another process
931
+ can bind it before your server does. Use strict-port behavior when supported
932
+ and verify the server bound the number supplied. Stim does not start,
933
+ supervise, or capture logs for these servers.
934
+
935
+ ports lists named labels and ports, plus Metro marked managed.
936
+ ports stop [label] kills TCP listeners on those named ports and releases
937
+ the allocations. It sends SIGTERM, waits two seconds, then SIGKILL if needed.
938
+ It prints the PID and command for each stopped process. The listener's cwd
939
+ can be anywhere: the named reservation is permission to stop that listener.
940
+ Reserve only services this workspace may stop. --dry-run prints what would
941
+ be stopped and released, without doing either. A failed inspection or stop
942
+ keeps that allocation for retry; other labels are still processed.
943
+ ports release [label] releases without signalling a process.
944
+ Omitting the label selects every named port, never Metro. stim stop leaves
945
+ named ports alone.
946
+
947
+ worktree remove stops named listeners and releases their allocations.
948
+ gc reports allocations whose workspace no longer exists; gc --delete stops
949
+ and releases them. Unmounted or unresolved workspace paths are retained.
950
+ Failed stops keep the registry entry. Use the same current Stim version for
951
+ cleanup: versions without ports do not know about named allocations.
952
+
953
+ A shared API does not get a shared reservation. Pass its port by environment
954
+ instead of allocating a separate label in every worktree.`
888
955
  },
889
956
  logs: {
890
957
  summary: "Querying the merged NDJSON timeline, and what --errors means",
@@ -898,10 +965,12 @@ discovered, not enumerated.
898
965
 
899
966
  EXIT 0 MEANS THE QUERY SUCCEEDED, whether or not records matched. A clean
900
967
  \`stim logs --errors\` check requires exit code 0 AND no matching errors in
901
- captured logs. An empty result does not prove launch or log capture succeeded;
902
- a workspace with no log directory also returns an empty result.
968
+ captured logs. A workspace that has never produced a timeline refuses with
969
+ STIM_NO_PROJECT instead of passing with an empty result. In a monorepo the
970
+ refusal names the nearest registered descendant app with logs, when there is one.
903
971
 
904
- For zero matches: STDOUT IS EMPTY, exit code 0, and
972
+ Once a timeline file exists, zero matches after filtering means STDOUT IS EMPTY,
973
+ exit code 0, and
905
974
  one dim note on STDERR reading \`No matching log records in <logs dir>\`
906
975
  (human mode only -- \`--json\` prints nothing at all, on either stream).
907
976
  The only exit-1 paths are a malformed query and no project.
@@ -1388,7 +1457,7 @@ code, never on the message.`,
1388
1457
  run the command again.`
1389
1458
  },
1390
1459
  STIM_CLAIM_REFUSED: {
1391
- summary: "a build lock exists whose holder cannot be identified; Stim neither removes it nor waits on it",
1460
+ summary: "an ownership claim blocks the operation; inspect its holder before removing an unresolved claim",
1392
1461
  body: () => `STIM_CLAIM_REFUSED
1393
1462
  A build lock records the holder's process IDENTITY, not just its pid, so a
1394
1463
  recycled pid reads as a gone builder rather than a live one, and a builder
@@ -1404,14 +1473,32 @@ code, never on the message.`,
1404
1473
  \`worktree warm\` reports it for the repository-wide warm claim under
1405
1474
  ~/.stim/warm-locks on both paths, including a \`--refresh\` that spawned its
1406
1475
  install and was killed before recording which process: nothing was refreshed
1407
- and nothing was copied. One case is NOT this code for a plain warm: a
1408
- STIM_HOME (or a warm-locks path under it) that is a file rather than a
1409
- directory. No claim can be stored there at all, which is a filesystem state
1410
- that predates claims, so plain \`warm\` degrades to the unsynchronised copy it
1411
- performed before them and \`--refresh\` still refuses.`
1476
+ and nothing was copied. A STIM_HOME or warm-locks ancestor that is a file
1477
+ also refuses. The message names that blocking path and prints a shell-quoted
1478
+ move-aside command. Inspect it first: the file may contain unrelated data.
1479
+ An existing backup prompts before overwriting; preserve both files.
1480
+ Android creation, recovery and teardown use per-AVD claims under
1481
+ ~/.stim/avd-locks. Creation records the avdmanager process group, so a claim
1482
+ left by a killed creator becomes recoverable after that group exits. Retry
1483
+ after a live creator or cleanup finishes. An interruption before the child
1484
+ is recorded, or during synchronous teardown, can leave no verifiable
1485
+ identity: inspect the named claim and confirm that neither Stim nor its
1486
+ avdmanager or adb child is still using the AVD before removing only that
1487
+ claim. Keep the AVD and its incomplete workspace record. Once the claim is
1488
+ safe to clear, retry \`stim android\` to reconcile incomplete setup through
1489
+ owned-device teardown. GC and project removal refuse while the claim is
1490
+ live or unresolved.
1491
+ Parked-device adoption and deletion use claims under $STIM_HOME/pool-locks.
1492
+ If a holder dies during a synchronous native callback, its child cannot be
1493
+ identified and the device stays protected, including from older CLIs through
1494
+ an opaque inline marker. Verify that the old Stim process
1495
+ and its native children have finished before removing the named claim.
1496
+ Legacy inline \`deletionClaim\` fields in config.json have no process
1497
+ identity and require separate manual inspection; \`stim guide lifecycle pool\`
1498
+ describes their field-only recovery.`
1412
1499
  },
1413
1500
  STIM_CLAIM_UNAVAILABLE: {
1414
- summary: "this process has no recordable identity, so no build lock or build slot can be taken at all",
1501
+ summary: "a process identity or warm claim store is unavailable, so the protected operation refuses",
1415
1502
  body: () => `STIM_CLAIM_UNAVAILABLE
1416
1503
  Every ownership claim records the holder's process identity, captured through
1417
1504
  the \`unique-pid\` native module. This code is that capture failing: no
@@ -1423,16 +1510,15 @@ code, never on the message.`,
1423
1510
  the same fingerprint while each believed it was alone. Reinstall Stim so the
1424
1511
  module for this platform is present, then run the command again. Nothing was
1425
1512
  built, installed or removed.
1426
- \`worktree warm --refresh\` refuses for the same reason, because it writes to
1427
- the source checkout. Plain \`worktree warm\` does NOT: it prints one dim
1428
- \`lock unavailable (...)\` line and copies unsynchronised. A copy only
1429
- reads, so running it with no claim is what it did before the lock existed,
1430
- while refusing it would break a warm that works today -- an unwritable
1431
- STIM_HOME included.
1432
- It still reads the claim set first, which takes no claim: a live \`--refresh\`
1433
- claim, or a claim it cannot resolve, makes even the unsynchronised copy
1434
- refuse, and the line names what it found. Only a repository nothing is
1435
- warming is copied without a claim.`
1513
+ Both plain \`worktree warm\` and \`worktree warm --refresh\` refuse before
1514
+ copying for the same reason. A copy without a claim would be invisible to a
1515
+ refresh starting after it, allowing the source dependencies to change while
1516
+ they are read.
1517
+ Warm also reports this code for EACCES, EPERM, EROFS and ENOENT while taking a claim.
1518
+ The message names the claim path and original filesystem error. Restore write
1519
+ access to the existing claim store, checking parent permissions, symlink
1520
+ targets, mount access and sandbox rules, then retry. Do not switch STIM_HOME to evade a claim:
1521
+ concurrent runs must coordinate through the same store.`
1436
1522
  },
1437
1523
  STIM_INSTALL_FAILED: {
1438
1524
  summary: "simctl, adb, or devicectl refused the artifact; the one signer-conflict retry",
@@ -1717,6 +1803,20 @@ so a Debug run on one is wired to a LAN origin instead of localhost.`,
1717
1803
  normally live under ~/.android/avd, and a booted AVD can use several GB. A
1718
1804
  boot whose emulator process exited is also reported at once rather than after
1719
1805
  the full cold-boot timeout.
1806
+ Before a local Android emulator boot, a memory phase line reports observed
1807
+ warning or critical macOS host memory pressure. If the initial boot wait
1808
+ times out under that pressure and the process this run started is still
1809
+ alive, Stim retries the WAIT once for up to 240 seconds more on the same
1810
+ serial; it never starts a second emulator. New-device preparation normally
1811
+ waits 120 seconds first; the later boot check waits 240 seconds. There is
1812
+ no extra wait for normal or unavailable pressure, a process that exited,
1813
+ or a process whose liveness this run cannot check. adb probes are bounded;
1814
+ final diagnostic queries share a separate budget of up to five seconds.
1815
+ A timeout remedy reports observed pressure and the running owned-device
1816
+ count when available. Device count alone does not establish memory pressure
1817
+ or trigger the extra wait. Stop an unneeded device with \`stim stop\` only
1818
+ in a workspace you own, then retry \`stim android\`; ask before closing
1819
+ other apps or devices. Specific emulator log errors retain their remedies.
1720
1820
 
1721
1821
  "this project's sim is X, but --device-type asked for Y"
1722
1822
  The project already owns a simulator of a different model, and Stim will
@@ -1984,7 +2084,9 @@ captured" (in metro.ndjson, bare RN)
1984
2084
  with no package.json above it, or one whose nearest package.json does not
1985
2085
  parse or depends on neither react-native nor expo, so the directory is not
1986
2086
  an app (the refusal names that package.json and says which of the two it
1987
- is; \`doctor\` reports the same directory as a finding), an
2087
+ is; \`doctor\` reports the same directory as a finding), a \`logs\` query in
2088
+ a workspace that has never produced a log timeline (the refusal names the
2089
+ nearest registered descendant app with logs when one exists), an
1988
2090
  android/app/build.gradle that declares product flavors with
1989
2091
  no variant selected (the refusal names the debug variants), or a
1990
2092
  \`--device-type\`, \`--runtime\` or \`--system-image\` name that is BLANK or
@@ -2035,8 +2137,21 @@ captured" (in metro.ndjson, bare RN)
2035
2137
  "Timed out waiting for the lock at <path>."
2036
2138
  Short directory locks serialize writes to config, workspace state, device
2037
2139
  leases, ownership records, metadata, and cache manifests. The path identifies
2038
- the lock. A lock older than 10s is taken over automatically. Wait for the
2039
- command holding it; if none is running, remove the named directory.`
2140
+ the lock. These locks wait up to 12s and never expire based on age. Wait for
2141
+ the command holding it; if none is running, remove the named directory.
2142
+ Short locks use the same process-identity claims as long operations, stored
2143
+ beside the visible directory at <path>.claims. An opaque marker in the visible
2144
+ directory also excludes older Stim versions. Only the exclusive claim holder
2145
+ can recover a recognized marker. A
2146
+ complete claim left by a proven-dead owner is recovered automatically on
2147
+ the next attempt. An unreadable or undecodable record instead reports
2148
+ STIM_CLAIM_REFUSED and names the claim to inspect; an unavailable native
2149
+ identity reports STIM_CLAIM_UNAVAILABLE without running the protected work.
2150
+ Older lock directories have no process identity. A visible directory left
2151
+ empty before marker publication or during final removal also cannot prove it is free.
2152
+ These paths still time out; verify that no holder is running before removing
2153
+ only the named directory. Standalone Metro and Expo cache packages use the
2154
+ same core protocol and do not require the Stim CLI.`
2040
2155
  },
2041
2156
  teardown: {
2042
2157
  summary: "an unmanaged port, an unverified supervisor, and a failed device teardown",
@@ -2142,24 +2257,14 @@ not on any remote" (worktree remove)
2142
2257
  which is usually the repository root, so every app of it reads the same one.`
2143
2258
  },
2144
2259
  warm: {
2145
- summary: "two warm refusals whose text is incomplete, known and not fixed",
2146
- body: () => `"Could not warm this worktree: EACCES: permission denied, mkdir
2147
- '<home>/warm-locks/<name>.lock'" (worktree warm --refresh)
2148
- A STIM_HOME that \`--refresh\` cannot write. The refusal itself is right -- it
2149
- writes to the source checkout, so it will not run without a claim -- but it
2150
- carries no \`failed: <CODE>\` line, because EACCES is not a Stim code, and no
2151
- fix line. Make STIM_HOME writable, or set STIM_HOME to somewhere writable,
2152
- then run it again. A plain warm degrades in this state rather than refusing.
2153
-
2154
- "... the claim path is a file, not a claim directory", with a \`rm -rf\` that
2155
- changes nothing (worktree warm --refresh)
2156
- When \`$STIM_HOME/warm-locks\` or STIM_HOME itself is a regular FILE, the
2157
- refusal names \`warm-locks/<name>.lock\` -- a path that cannot exist under a
2158
- file -- so running the printed removal does nothing and the next run refuses
2159
- identically. Remove the file that is in the way and run it again. A plain
2160
- warm copies unsynchronised in this state.
2161
-
2162
- Both are documented rather than fixed: appandflow/stim#696.`
2260
+ summary: "worktree warm claim storage refusals and recovery",
2261
+ body: () => `Both plain warm and --refresh require an ownership claim.
2262
+ Permission-denied or read-only claim storage reports STIM_CLAIM_UNAVAILABLE
2263
+ and names the path. Restore access to the existing claim store and retry.
2264
+ A non-directory claim ancestor reports STIM_CLAIM_REFUSED and names the
2265
+ blocking file. Inspect it and use the printed move-aside command to preserve
2266
+ its contents before retrying. See guide errors STIM_CLAIM_UNAVAILABLE and
2267
+ guide errors STIM_CLAIM_REFUSED for the recovery details.`
2163
2268
  },
2164
2269
  carry: {
2165
2270
  summary: "worktree warm copy results, lockfile mismatches, and remedies",
@@ -2340,6 +2445,7 @@ a seed belongs to this workflow.
2340
2445
  # For a clean check, require exit 0 AND no matching errors.
2341
2446
  # Human mode prints "No matching log records" on stderr for zero matches.
2342
2447
  # Exit 0 alone means the query succeeded, even when it printed errors.
2448
+ # A workspace with no captured timeline refuses with STIM_NO_PROJECT.
2343
2449
  stim logs --errors
2344
2450
 
2345
2451
  # 5. Edit the JS. Fast Refresh applies it; no Stim command is involved.
@@ -2435,6 +2541,12 @@ ANDROID EMULATOR RESTARTS
2435
2541
  Rerun \`stim android\` with the same build options in that workspace to restore Metro
2436
2542
  forwarding, then reopen agent-device on the serial that run reports.
2437
2543
  Other workspaces' simulators and automation sessions remain theirs.
2544
+ A local emulator boot reports observed warning or critical macOS memory
2545
+ pressure before starting. A timeout under elevated pressure gets one
2546
+ additional wait of up to 240 seconds only while the process this run
2547
+ started is alive. Stim keeps waiting on the same emulator; it never
2548
+ launches a duplicate. See \`stim guide errors STIM_NO_DEVICE\` for the
2549
+ wait windows, diagnostics, and recovery steps.
2438
2550
 
2439
2551
  DESTRUCTIVE COMMANDS -- ask the user first
2440
2552
  gc --delete deletes orphaned stim-* devices, tens of GB
@@ -2707,9 +2819,11 @@ result as proof instead of requiring an unrelated screenshot.`
2707
2819
 
2708
2820
  \`--refresh\` always prints a \`lock\` line, then its own facts, one per
2709
2821
  step, before those. A plain warm prints the \`lock\` line only when its copy
2710
- actually waited for another warm:
2822
+ actually waited for another warm. A current refresh reports
2823
+ \`lock acquired shared (seed current)\`; a refresh needing changes
2824
+ reports \`acquired exclusive\` with the reason from its initial check:
2711
2825
 
2712
- lock acquired (waited 12s for stim worktree warm --refresh pid 41233) -- stim guide lifecycle options
2826
+ lock acquired exclusive (fast-forward 2 commits) (waited 12s for stim worktree warm --refresh pid 41233) -- stim guide lifecycle options
2713
2827
  checkout janic/wip 2 commits behind origin/janic/wip -> fast-forwarded to 4b81e0c
2714
2828
  not the default branch (main); worktrees seeded from this copy
2715
2829
  carry janic/wip's dependencies
@@ -2794,6 +2908,22 @@ result as proof instead of requiring an unrelated screenshot.`
2794
2908
  retry it. The reported pool can temporarily exceed the bound rather than
2795
2909
  orphaning a simulator.
2796
2910
 
2911
+ Adoption and deletion share a process-identity claim for each parked iOS
2912
+ simulator or Android emulator under $STIM_HOME/pool-locks. A live owner
2913
+ excludes both operations. A proven dead or different owner is recovered on
2914
+ the next attempt, unless it may have left native work running. Deletion also
2915
+ puts an opaque marker on the pool record so older Stim versions keep it
2916
+ protected. New versions resolve that marker through the same identity claim.
2917
+ A crash during a synchronous device-tool call leaves that work unverifiable; inspect
2918
+ the old process and its native children before following the claim's removal
2919
+ remedy. See \`stim guide errors STIM_CLAIM_REFUSED\`.
2920
+
2921
+ Older inline \`deletionClaim\` markers contain only a pid and stay protected,
2922
+ even when that pid is absent. After verifying that neither the old Stim
2923
+ process nor its native child is using the device, remove only that parked
2924
+ record's \`deletionClaim\` field from config.json, then retry. Keep the pool
2925
+ record and device; do not remove the whole config.
2926
+
2797
2927
  and an adopting run says so where a plain boot would say \`booted\`:
2798
2928
 
2799
2929
  device stim-app-412 (iPhone 17 26.5) (9C1F..) adopted (11s)
@@ -2837,6 +2967,14 @@ result as proof instead of requiring an unrelated screenshot.`
2837
2967
  When a fresh AVD's preferred name belongs to another workspace or a parked
2838
2968
  entry, Stim adds a short suffix instead of taking over that device. Always
2839
2969
  use the actual device name and serial reported by the platform command.
2970
+ Creation reserves the owned AVD before running native tools, so other
2971
+ workspaces can update config while GC and teardown retain that reservation.
2972
+ Interrupted creation keeps an incomplete record and protects its recorded
2973
+ native process group until it exits. Retry \`stim android\` for owned-device
2974
+ cleanup and recreation; a live or unresolved per-AVD claim
2975
+ refuses cleanup. Follow \`stim guide errors STIM_CLAIM_REFUSED\` before
2976
+ manually clearing a claim, and keep the AVD data while its process state
2977
+ cannot be verified.
2840
2978
 
2841
2979
  Android cleanup happens AFTER boot, before install or launch: \`adb shell
2842
2980
  pm clear\` clears the adopting app's data while retaining its APK, and
@@ -3129,6 +3267,20 @@ WHAT MAKES THE CACHE ACTUALLY HIT: .FINGERPRINTIGNORE
3129
3267
  Never ignore a real native input -- a Podfile, a gradle file, the app config
3130
3268
  -- to force a hit: that trades a slow build for a wrong one.
3131
3269
 
3270
+
3271
+ React Native Test App can generate checkout-specific Android output under
3272
+ node_modules/react-native-test-app/android/support/build. If fingerprint
3273
+ differences point only there, add these project-specific entries:
3274
+
3275
+ node_modules/react-native-test-app/android/support/build
3276
+ node_modules/react-native-test-app/android/support/build/**/*
3277
+
3278
+ Put them in the app root's .fingerprintignore and use the path as it appears
3279
+ in the fingerprint sources; a hoisted or linked dependency may differ.
3280
+ Keep android/support/build.gradle and native sources included. Confirm equal
3281
+ fingerprints across built worktrees, and confirm a real native input edit
3282
+ still changes the hash. These exclusions are not Stim defaults.
3283
+
3132
3284
  \`stim doctor\` measures this directly rather than reading the file: it
3133
3285
  fingerprints HEAD in a temporary clean worktree, compares, and reports a
3134
3286
  mismatch naming the differing sources. Untracked, non-gitignored files under
@@ -3274,8 +3426,9 @@ OPT-IN CONCURRENCY LIMITS (UNLIMITED BY DEFAULT)
3274
3426
  Running it in the source checkout refuses.
3275
3427
 
3276
3428
  \`--refresh\` WRITES TO THE SOURCE CHECKOUT before the copy, which is why it
3277
- is opt-in: it fetches, fast-forwards whatever branch is checked out there to
3278
- its \`@{upstream}\`, and installs what the new commits moved. It refuses a
3429
+ is opt-in: it checks the upstream, fetches changes when needed, and fast-forwards
3430
+ whatever branch is checked out there to its \`@{upstream}\`, and installs
3431
+ what the new commits moved. It refuses a
3279
3432
  source checkout it cannot move -- uncommitted changes to tracked files or a
3280
3433
  rebase or merge in progress (STIM_MAIN_DIRTY), a detached HEAD
3281
3434
  (STIM_MAIN_DETACHED), or a branch that is both ahead and behind
@@ -3309,9 +3462,15 @@ OPT-IN CONCURRENCY LIMITS (UNLIMITED BY DEFAULT)
3309
3462
  writes no failure anywhere -- is recovered the same way a failed one is.
3310
3463
 
3311
3464
  One lock per repository serialises this, whether or not you pass the flag:
3312
- \`--refresh\` holds it exclusively, and every copy holds it shared, so a copy
3313
- can never read a node_modules a refresh is rewriting. Two plain warms of the
3314
- same repository run together. The lock is keyed on the repository root, so
3465
+ \`--refresh\` first checks under a shared claim. It uses \`git ls-remote\`
3466
+ to compare the upstream's advertised commit with the local tracking ref without
3467
+ updating Git refs or FETCH_HEAD. When the checkout, dependencies and Pods are
3468
+ current, it keeps that shared claim through the copy, overlapping other copies.
3469
+ A changed or unconfirmed upstream, fast-forward, dependency install or Pods
3470
+ install requires an exclusive claim. It releases shared before waiting for
3471
+ exclusive, then checks the checkout again before fetching or writing. Every
3472
+ copy holds shared, so no copy reads a node_modules a refresh is rewriting.
3473
+ The lock is keyed on the repository root, so
3315
3474
  two apps of one monorepo share it. It is the same ownership claim the build
3316
3475
  locks take: the holder's process IDENTITY decides, so a holder that dies frees
3317
3476
  it, a recycled pid cannot keep it, and a refresh whose install runs in a
@@ -3319,20 +3478,13 @@ OPT-IN CONCURRENCY LIMITS (UNLIMITED BY DEFAULT)
3319
3478
  holder every 30s and gives up with STIM_LOCK_TIMEOUT; a claim Stim cannot
3320
3479
  resolve refuses with STIM_CLAIM_REFUSED and names the removal rather than
3321
3480
  waiting on it or removing it.
3322
- A plain warm that cannot record a claim at all -- an unwritable STIM_HOME, a
3323
- STIM_HOME on a read-only mount or under a dangling symlink, a STIM_HOME that
3324
- is a file, or no \`unique-pid\` build for this platform
3325
- (STIM_CLAIM_UNAVAILABLE) -- says so in one dim line and copies without it,
3326
- exactly as it did before the lock existed, because a copy only reads.
3327
- \`--refresh\` refuses instead, because it writes. Before that unsynchronised
3328
- copy starts, warm still READS the claim set, which needs no claim of its own:
3329
- if a refresh holds this repository, or a claim in it cannot be resolved, the
3330
- copy refuses rather than reading a tree that is being rewritten. That read is
3331
- not a lock, so a refresh that starts after it is not covered; it closes the
3332
- window in which one is already installing. Closing the rest needs a claim,
3333
- which is the one thing that state cannot record, so the residual is
3334
- documented rather than fixed (appandflow/stim#696), as is the
3335
- \`acquired (waited 0s ...)\` a contended-but-fast wait prints.
3481
+ Both plain warm and --refresh refuse before copying when no claim can be
3482
+ recorded. Missing process identity, denied write access and read-only storage
3483
+ report STIM_CLAIM_UNAVAILABLE with recovery instructions. Restore access to the
3484
+ same claim store before retrying; choosing a different STIM_HOME would hide
3485
+ concurrent warm operations. A non-directory claim path reports
3486
+ STIM_CLAIM_REFUSED and names the blocking file with a move-aside command that
3487
+ preserves its contents. Sub-second waits omit the elapsed-time suffix.
3336
3488
 
3337
3489
  Wait for warm to exit successfully (exit code 0) before running start,
3338
3490
  ios, android, or a dependency install in that worktree. If a shell tool
@@ -3814,6 +3966,16 @@ Those are the only two commands that delete. \`stim stop\` shuts a device
3814
3966
  DOWN and leaves it assigned, which is what makes returning to a branch cost a
3815
3967
  boot rather than a create, a provision and a reinstall.
3816
3968
 
3969
+ Before shutting down, parking or deleting an owned device, Stim best-effort
3970
+ closes local agent-device sessions bound to its exact iOS UDID or live Android
3971
+ serial. It rechecks device ownership and uses agent-device's rejecting session
3972
+ target guard; sessions on other devices stay open. Physical devices are outside
3973
+ this cleanup. agent-device is optional: a missing binary skips this step, and a
3974
+ failed or timed-out list/close prints a device line on stderr while teardown
3975
+ continues. Cleanup allows at most 15 seconds of agent-device calls per device.
3976
+ A local daemon that cannot use socket transport or a CLI without the target guard
3977
+ skips cleanup with a warning; no remote daemon is used.
3978
+
3817
3979
  Neither touches $STIM_HOME/stats.json: \`gc\` never reports or trims the run
3818
3980
  counters \`stats\` prints, and there is no reset flag. Delete that one file to
3819
3981
  start the counters over. A file this version cannot read -- unparseable, or
@@ -3831,6 +3993,13 @@ to stats.json.corrupt-<unix ms> and starts a new one.`,
3831
3993
  branches stay. A branch with an existing Stim ownership record is deleted
3832
3994
  only when it has no unique commits.
3833
3995
 
3996
+ NAMED SERVER PORTS
3997
+ worktree remove stops TCP listeners on each named allocation and releases
3998
+ the ports. gc reports named allocations for missing workspaces; gc --delete
3999
+ stops their listeners and releases them. Unmounted or unresolved paths stay
4000
+ registered. Failed stops retain their allocations for a later retry.
4001
+ stim stop does not touch named ports. See guide ports.
4002
+
3834
4003
  ON THE SOURCE CHECKOUT
3835
4004
  git cannot remove a repository's main working tree, and deleting the source
3836
4005
  checkout is not what anyone meant -- so there, and only there,