peachsim 0.1.527

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 (324) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +640 -0
  3. package/cli/app-config.ts +82 -0
  4. package/cli/app-fonts.ts +388 -0
  5. package/cli/app-project.ts +59 -0
  6. package/cli/app-splash.ts +171 -0
  7. package/cli/app-state-reset.ts +26 -0
  8. package/cli/auth.ts +184 -0
  9. package/cli/bin.ts +59 -0
  10. package/cli/bridge-diagnostics.ts +226 -0
  11. package/cli/bridge-flow-runner.ts +2806 -0
  12. package/cli/browser-backend.ts +44 -0
  13. package/cli/browser-evals.ts +96 -0
  14. package/cli/cli-update.ts +261 -0
  15. package/cli/cloud-client.ts +1530 -0
  16. package/cli/cloud-dispatch.ts +206 -0
  17. package/cli/cloud-session.ts +227 -0
  18. package/cli/command-contract.ts +221 -0
  19. package/cli/command-registry.ts +304 -0
  20. package/cli/commands/agent-wrapper.ts +1048 -0
  21. package/cli/commands/agent.ts +439 -0
  22. package/cli/commands/app-fonts.ts +99 -0
  23. package/cli/commands/assert.ts +542 -0
  24. package/cli/commands/auth.ts +71 -0
  25. package/cli/commands/box/bundle-plane.ts +71 -0
  26. package/cli/commands/box/checkout-plane.ts +256 -0
  27. package/cli/commands/box/plane-share.ts +208 -0
  28. package/cli/commands/box/rnx-command.ts +60 -0
  29. package/cli/commands/box/search-command.ts +157 -0
  30. package/cli/commands/box.ts +925 -0
  31. package/cli/commands/camera.ts +266 -0
  32. package/cli/commands/cleanup.ts +169 -0
  33. package/cli/commands/compat.ts +176 -0
  34. package/cli/commands/config.ts +34 -0
  35. package/cli/commands/control.ts +2154 -0
  36. package/cli/commands/cpu-profile.ts +941 -0
  37. package/cli/commands/daemon-mac-app.ts +165 -0
  38. package/cli/commands/daemon.ts +800 -0
  39. package/cli/commands/debug.ts +751 -0
  40. package/cli/commands/desktop.ts +39 -0
  41. package/cli/commands/detect.ts +201 -0
  42. package/cli/commands/detox.ts +481 -0
  43. package/cli/commands/device.ts +134 -0
  44. package/cli/commands/diagnose.ts +589 -0
  45. package/cli/commands/do-chain.ts +408 -0
  46. package/cli/commands/electron.ts +94 -0
  47. package/cli/commands/film.ts +379 -0
  48. package/cli/commands/flow.ts +1240 -0
  49. package/cli/commands/inspect/actions.ts +516 -0
  50. package/cli/commands/inspect/core.ts +2086 -0
  51. package/cli/commands/inspect/count.ts +17 -0
  52. package/cli/commands/inspect/describe.ts +119 -0
  53. package/cli/commands/inspect/env.ts +25 -0
  54. package/cli/commands/inspect/find.ts +169 -0
  55. package/cli/commands/inspect/get-layout.ts +53 -0
  56. package/cli/commands/inspect/keyboard.ts +54 -0
  57. package/cli/commands/inspect/list.ts +175 -0
  58. package/cli/commands/inspect/memory.ts +215 -0
  59. package/cli/commands/inspect/redaction.ts +39 -0
  60. package/cli/commands/inspect/resolve-target.ts +77 -0
  61. package/cli/commands/inspect/screens.ts +78 -0
  62. package/cli/commands/inspect/settle.ts +32 -0
  63. package/cli/commands/inspect/settling.ts +197 -0
  64. package/cli/commands/inspect/shared.ts +259 -0
  65. package/cli/commands/inspect/sleep.ts +15 -0
  66. package/cli/commands/inspect/tree.ts +23 -0
  67. package/cli/commands/inspect/url.ts +17 -0
  68. package/cli/commands/inspect/wait-event.ts +211 -0
  69. package/cli/commands/inspect/wait-idle.ts +25 -0
  70. package/cli/commands/inspect/wait-ready.ts +87 -0
  71. package/cli/commands/inspect/wait-selector.ts +59 -0
  72. package/cli/commands/inspect/wait.ts +32 -0
  73. package/cli/commands/inspect.ts +4738 -0
  74. package/cli/commands/install-cli.ts +275 -0
  75. package/cli/commands/install-desktop.ts +385 -0
  76. package/cli/commands/login.ts +357 -0
  77. package/cli/commands/logout.ts +24 -0
  78. package/cli/commands/maestro-generate.ts +375 -0
  79. package/cli/commands/maestro.ts +536 -0
  80. package/cli/commands/mode.ts +58 -0
  81. package/cli/commands/no-bridge-hint.ts +80 -0
  82. package/cli/commands/perf.ts +67 -0
  83. package/cli/commands/permissions.ts +203 -0
  84. package/cli/commands/platform.ts +410 -0
  85. package/cli/commands/preview.ts +103 -0
  86. package/cli/commands/profile.ts +106 -0
  87. package/cli/commands/react.ts +353 -0
  88. package/cli/commands/record.ts +1435 -0
  89. package/cli/commands/report-issue.ts +305 -0
  90. package/cli/commands/reset.ts +89 -0
  91. package/cli/commands/runtime.ts +348 -0
  92. package/cli/commands/screenshot-command.ts +106 -0
  93. package/cli/commands/screenshot-layers.ts +144 -0
  94. package/cli/commands/screenshot-mode.ts +38 -0
  95. package/cli/commands/screenshot.ts +492 -0
  96. package/cli/commands/screenshots-capture.ts +608 -0
  97. package/cli/commands/screenshots.ts +127 -0
  98. package/cli/commands/serve.ts +139 -0
  99. package/cli/commands/setup.ts +416 -0
  100. package/cli/commands/shell-boolean-mode.ts +82 -0
  101. package/cli/commands/skills.ts +468 -0
  102. package/cli/commands/slides.ts +357 -0
  103. package/cli/commands/state.ts +94 -0
  104. package/cli/commands/storage.ts +58 -0
  105. package/cli/commands/telemetry.ts +55 -0
  106. package/cli/commands/test.ts +535 -0
  107. package/cli/commands/three-mode.ts +771 -0
  108. package/cli/commands/timeline.ts +121 -0
  109. package/cli/commands/upgrade.ts +167 -0
  110. package/cli/commands/upload.ts +1203 -0
  111. package/cli/commands/version.ts +63 -0
  112. package/cli/commands/welcome.ts +17 -0
  113. package/cli/commands/what-happened.ts +328 -0
  114. package/cli/current-sim.ts +217 -0
  115. package/cli/desktop-companion.ts +315 -0
  116. package/cli/drivers/electron.ts +70 -0
  117. package/cli/drivers/index.ts +20 -0
  118. package/cli/drivers/playwright-provisioning.ts +184 -0
  119. package/cli/drivers/playwright-sim-host.ts +537 -0
  120. package/cli/drivers/playwright.ts +209 -0
  121. package/cli/drivers/registry.ts +65 -0
  122. package/cli/drivers/types.ts +102 -0
  123. package/cli/flow-export.ts +55 -0
  124. package/cli/flow-file.ts +142 -0
  125. package/cli/flow-live-status.ts +126 -0
  126. package/cli/flow-session.ts +140 -0
  127. package/cli/help-core.ts +143 -0
  128. package/cli/help.ts +44 -0
  129. package/cli/hidden-runtime-alias.ts +19 -0
  130. package/cli/hints.ts +214 -0
  131. package/cli/inspect-notice-state.ts +114 -0
  132. package/cli/internal-child.ts +176 -0
  133. package/cli/maestro-js.ts +323 -0
  134. package/cli/main.ts +704 -0
  135. package/cli/open-url.ts +8 -0
  136. package/cli/outbound-endpoints.ts +830 -0
  137. package/cli/parent-pid.ts +204 -0
  138. package/cli/parse-args.ts +189 -0
  139. package/cli/privacy.ts +53 -0
  140. package/cli/prompt.ts +56 -0
  141. package/cli/recording-access.ts +108 -0
  142. package/cli/registry.ts +2 -0
  143. package/cli/resolve-assets.ts +63 -0
  144. package/cli/rnx-in-process.ts +98 -0
  145. package/cli/run-registry.ts +226 -0
  146. package/cli/run-rnx-core.ts +148 -0
  147. package/cli/run-rnx.ts +137 -0
  148. package/cli/runtime-notes.ts +66 -0
  149. package/cli/runtime-summary.ts +25 -0
  150. package/cli/secure-file.ts +31 -0
  151. package/cli/self-invocation.ts +58 -0
  152. package/cli/send-to-box.ts +109 -0
  153. package/cli/setup-repository.ts +191 -0
  154. package/cli/shell-init.ts +98 -0
  155. package/cli/standalone.ts +4 -0
  156. package/cli/telemetry.ts +84 -0
  157. package/cli/ui.ts +68 -0
  158. package/cli/ws-bridge.ts +1020 -0
  159. package/detox/attempt-events.cjs +23 -0
  160. package/detox/colors.ts +54 -0
  161. package/detox/config-loader.ts +135 -0
  162. package/detox/element-types.ts +48 -0
  163. package/detox/expectations.ts +534 -0
  164. package/detox/gestures.ts +501 -0
  165. package/detox/index.ts +2694 -0
  166. package/detox/jest-preset.cjs +29 -0
  167. package/detox/jest-setup-after-env.cjs +22 -0
  168. package/detox/matchers.ts +30 -0
  169. package/detox/motion-change.cjs +109 -0
  170. package/detox/motion-change.d.cts +35 -0
  171. package/detox/navigation-lifecycle.ts +290 -0
  172. package/detox/navigation.ts +43 -0
  173. package/detox/proof-frame.cjs +114 -0
  174. package/detox/record-block-execution.cjs +78 -0
  175. package/detox/screenshots/animated-color-test-rest-norngh.png +0 -0
  176. package/detox/screenshots/color-test-after-drag-norngh.png +0 -0
  177. package/detox/screenshots/color-test-rest-norngh.png +0 -0
  178. package/detox/screenshots/theme-blue-toggle.png +0 -0
  179. package/detox/screenshots/theme-blue.png +0 -0
  180. package/detox/screenshots/theme-red-toggle.png +0 -0
  181. package/detox/screenshots/theme-red.png +0 -0
  182. package/dist-lib/agent-daemon-client.cjs +502 -0
  183. package/dist-lib/agent-events.cjs +48 -0
  184. package/dist-lib/agent-identity.cjs +53 -0
  185. package/dist-lib/agent-sessions.cjs +759 -0
  186. package/dist-lib/attached-projects.cjs +526 -0
  187. package/dist-lib/auth/shared-session.cjs +208 -0
  188. package/dist-lib/backend-origin.cjs +88 -0
  189. package/dist-lib/beta.cjs +41 -0
  190. package/dist-lib/beta.mjs +13 -0
  191. package/dist-lib/bridge-constants.cjs +161 -0
  192. package/dist-lib/bridge-contract-input.cjs +451 -0
  193. package/dist-lib/bridge-contract-input.mjs +414 -0
  194. package/dist-lib/bridge-contract.cjs +130 -0
  195. package/dist-lib/bridge-contract.mjs +94 -0
  196. package/dist-lib/capture-contract.cjs +220 -0
  197. package/dist-lib/capture-contract.mjs +190 -0
  198. package/dist-lib/cli-constants.cjs +32 -0
  199. package/dist-lib/cloud-contract.cjs +194 -0
  200. package/dist-lib/cloud-contract.mjs +145 -0
  201. package/dist-lib/cloud.cjs +670 -0
  202. package/dist-lib/cloud.mjs +639 -0
  203. package/dist-lib/config.cjs +96 -0
  204. package/dist-lib/detox/index.cjs +3789 -0
  205. package/dist-lib/detox/jest-preset.cjs +29 -0
  206. package/dist-lib/dev-bundle-resolution.cjs +290 -0
  207. package/dist-lib/home-paths.cjs +638 -0
  208. package/dist-lib/host/bridge-host.cjs +10903 -0
  209. package/dist-lib/host/fetch-proxy-handler.cjs +421 -0
  210. package/dist-lib/host/fetch-proxy-overrides.cjs +63 -0
  211. package/dist-lib/host/fetch-proxy-overrides.mjs +37 -0
  212. package/dist-lib/host/replacement-module-handler.cjs +469 -0
  213. package/dist-lib/host/websocket-proxy.cjs +298 -0
  214. package/dist-lib/index.cjs +4268 -0
  215. package/dist-lib/jump-to-source-babel.cjs +93 -0
  216. package/dist-lib/jump-to-source-native.cjs +112 -0
  217. package/dist-lib/menu.cjs +646 -0
  218. package/dist-lib/menu.mjs +604 -0
  219. package/dist-lib/metro-fingerprint-registry.cjs +186 -0
  220. package/dist-lib/metro-fingerprint-registry.mjs +156 -0
  221. package/dist-lib/metro-production-bundle.cjs +329 -0
  222. package/dist-lib/metro-production-bundle.mjs +295 -0
  223. package/dist-lib/metro.cjs +904 -0
  224. package/dist-lib/profiles.cjs +246 -0
  225. package/dist-lib/public-brand.cjs +39 -0
  226. package/dist-lib/react-native-host-modules.cjs +51 -0
  227. package/dist-lib/react-native-host-modules.mjs +25 -0
  228. package/dist-lib/render-mode.cjs +54 -0
  229. package/dist-lib/scripts/dev-server-scanner.cjs +1232 -0
  230. package/dist-lib/sdk.cjs +2433 -0
  231. package/dist-lib/sdk.mjs +2336 -0
  232. package/dist-lib/skills.cjs +39132 -0
  233. package/dist-lib/swift-submit.cjs +92 -0
  234. package/dist-lib/swift-submit.mjs +65 -0
  235. package/dist-lib/swift.cjs +1646 -0
  236. package/dist-lib/vite.cjs +1591 -0
  237. package/package.json +290 -0
  238. package/scripts/dev-server-scanner.ts +1291 -0
  239. package/scripts/optional-demo-registry.ts +113 -0
  240. package/skills/contrast/SKILL.md +165 -0
  241. package/skills/rnx-debug/SKILL.md +433 -0
  242. package/skills/rnx-perf/SKILL.md +399 -0
  243. package/skills/rnx-setup/SKILL.md +172 -0
  244. package/skills/rnx-test/SKILL.md +321 -0
  245. package/skills/rnx-visual/SKILL.md +126 -0
  246. package/src/agent-daemon-client.ts +410 -0
  247. package/src/agent-events.ts +71 -0
  248. package/src/agent-identity.ts +23 -0
  249. package/src/agent-prompt.ts +71 -0
  250. package/src/agent-sessions.ts +580 -0
  251. package/src/app-url.ts +39 -0
  252. package/src/attached-projects.ts +539 -0
  253. package/src/auth/shared-session.ts +235 -0
  254. package/src/backend-origin.ts +70 -0
  255. package/src/beta.ts +19 -0
  256. package/src/bridge-constants.ts +175 -0
  257. package/src/bridge-contract-input.ts +502 -0
  258. package/src/bridge-contract.ts +558 -0
  259. package/src/browser-cache.ts +16 -0
  260. package/src/capture-contract.ts +587 -0
  261. package/src/cli-constants.ts +1 -0
  262. package/src/cli-version.ts +40 -0
  263. package/src/cloud-contract.ts +274 -0
  264. package/src/cloud.ts +762 -0
  265. package/src/codex-client.ts +234 -0
  266. package/src/config.ts +132 -0
  267. package/src/connect.ts +101 -0
  268. package/src/dev-bundle-resolution.ts +346 -0
  269. package/src/dev-server-open.ts +198 -0
  270. package/src/disk-cleanup.ts +540 -0
  271. package/src/flow-timeout-scale.ts +15 -0
  272. package/src/home-paths.ts +806 -0
  273. package/src/host/agent-host.ts +666 -0
  274. package/src/host/bridge-host.ts +2788 -0
  275. package/src/host/fetch-proxy-handler.ts +449 -0
  276. package/src/host/fetch-proxy-overrides.ts +51 -0
  277. package/src/host/open-url.ts +89 -0
  278. package/src/host/plane-host.ts +203 -0
  279. package/src/host/replacement-module-handler.ts +158 -0
  280. package/src/host/websocket-proxy.ts +315 -0
  281. package/src/index.ts +71 -0
  282. package/src/jump-to-source-babel.ts +135 -0
  283. package/src/jump-to-source-native.ts +102 -0
  284. package/src/menu.ts +777 -0
  285. package/src/metro-fingerprint-registry.ts +302 -0
  286. package/src/metro-plugin.ts +341 -0
  287. package/src/metro-production-bundle.ts +471 -0
  288. package/src/native-dev-bundle-url.ts +118 -0
  289. package/src/native-seam-manifest.ts +341 -0
  290. package/src/playwright-host-log.ts +16 -0
  291. package/src/plist.ts +27 -0
  292. package/src/poll-until.ts +123 -0
  293. package/src/profiles.ts +179 -0
  294. package/src/public-brand.ts +9 -0
  295. package/src/react-native-host-modules.ts +26 -0
  296. package/src/render-mode.ts +27 -0
  297. package/src/rolldown-plugin-swift.ts +240 -0
  298. package/src/runtime-assets.ts +233 -0
  299. package/src/runtime-delivery.ts +54 -0
  300. package/src/screenshots/compose.ts +422 -0
  301. package/src/screenshots/frame-compose.ts +514 -0
  302. package/src/screenshots/orchestrate.ts +256 -0
  303. package/src/screenshots/org-deck.ts +91 -0
  304. package/src/screenshots/registry.ts +58 -0
  305. package/src/screenshots/schema.ts +364 -0
  306. package/src/sdk.ts +15 -0
  307. package/src/settings-bundle.ts +418 -0
  308. package/src/shell-bridge-identity.ts +15 -0
  309. package/src/sim-client.ts +697 -0
  310. package/src/skills/builtin/a11y-review.ts +132 -0
  311. package/src/skills/builtin/compat-check.ts +71 -0
  312. package/src/skills/builtin/maestro-test.ts +121 -0
  313. package/src/skills/builtin/perf-profile.ts +90 -0
  314. package/src/skills/builtin/screenshot-all.ts +52 -0
  315. package/src/skills/builtin/visual-diff.ts +100 -0
  316. package/src/skills/registry.ts +107 -0
  317. package/src/skills/types.ts +41 -0
  318. package/src/swift-submit.ts +185 -0
  319. package/src/swift.ts +7 -0
  320. package/src/team-machine-prompt.ts +73 -0
  321. package/src/vite-plugin-one.ts +264 -0
  322. package/src/vite-plugin-swift.ts +620 -0
  323. package/src/vite-plugin.ts +1946 -0
  324. package/src/worklets-babel.ts +250 -0
@@ -0,0 +1,399 @@
1
+ ---
2
+ name: rnx-perf
3
+ description: Optimize rnx engine and app performance — scroll jank, dropped frames, slow animation, idle CPU, memory growth, and "smooth normally but laggy under load" (video calls, busy machines). Covers measuring on the right thread with the right browser, proving each perf tier actually engages via its counters, a CPU-contention test harness, and the map of existing perf machinery so you improve it instead of reinventing it. For general app debugging load /rnx-debug first.
4
+ ---
5
+
6
+ # rnx performance
7
+
8
+ optimize performance in rnx: the shell frame pipeline, the compositor
9
+ tiers, and the guest app. this skill exists because every real perf win in
10
+ this engine followed the same loop — measure on the right thread, read the
11
+ counters that prove machinery engaged, fix the one thing the counters
12
+ indict, remeasure — and every wasted week came from skipping a step.
13
+
14
+ > needs a connected, pinned sim (`/rnx-setup`). general "what's wrong"
15
+ > triage lives in `/rnx-debug`; this skill goes deeper on perf only.
16
+
17
+ ## the four laws (each paid for by a real incident)
18
+
19
+ 1. **measure the worker that painted it, in the right browser.** the tenant
20
+ hasn't painted since the two-worker split, so never read a tenant sampler
21
+ (the one reporting `avg 0.48ms (2069 fps)` for a visibly janky scroll was
22
+ removed). but "the shell" is no longer the whole answer either: the
23
+ compositor worker owns `home`, `app:one` and `app:two`, so **every guest-app
24
+ draw call, paint boundary and raster-tier decision is in the compositor's
25
+ render profile, not the shell's.** `perf shell stop` prints both, labelled
26
+ by worker — read the compositor block for anything inside an app. for
27
+ months it printed only the shell's, and a dense token-list fling
28
+ therefore reported `raster tier: 0 promotions / 0 blits` while the
29
+ compositor was blitting ~14 rows a frame; the giveaway was `node visits: 7`
30
+ next to `app:one 1014 paints`. if a render-profile block describes a
31
+ handful of nodes while an app surface is painting hundreds of times, you
32
+ are reading the wrong worker.
33
+ browser matters just as much: playwright's bundled headless chromium has
34
+ NO GPU — CanvasKit falls back to software rendering and every number lies
35
+ high. launch a dedicated GPU-backed Chrome executable with a disposable
36
+ profile and an owning process supervisor that terminates its exact process
37
+ tree. never automate the user's own browser profile.
38
+ 2. **machinery silently not engaging is the default failure mode.** the
39
+ raster tier compiled, gated, and did *nothing* for months — zero
40
+ promotions on its textbook case — until per-gate rejection counters
41
+ existed. the frame-demand gate was blind to commit storms. the vsync
42
+ pump flapped 100 msgs/s at rest. never assume a tier works because it's
43
+ wired; read its counter during the exact workload.
44
+ 3. **check reclaimable memory and sampled CPU before blaming code.** an OOM or
45
+ busy machine mimics every perf and infra failure. on macOS, read
46
+ `memory_pressure -Q` and take multiple `iostat -c 3` CPU samples; on Linux,
47
+ read `MemAvailable` from `/proc/meminfo` and sample CPU idle over time. keep
48
+ the busiest sample. raw macOS `freemem()` and load average are not admission
49
+ signals. attribute browser processes with `ps -eo pid,ppid,args` before
50
+ treating one as abandoned or stopping an exact pid.
51
+ 4. **never trade animation fidelity for CPU.** perpetual sub-frame animation
52
+ must keep its authored cadence. attack per-frame cost (layers, caching,
53
+ damage), never cadence or fidelity.
54
+
55
+ ## measuring
56
+
57
+ ### presentation proof must stay outside the simulator
58
+
59
+ Performance acceptance must not use `rnx record`, `liveComposite`,
60
+ `captureBitmaps`, Chrome screencast, or another in-page frame-copy path. Those
61
+ paths add work to the simulator or browser and can change the cadence being
62
+ measured. Record the visible window with the operating system's native screen
63
+ capture instead.
64
+
65
+ Use an operating-system-owned window recorder. Target the exact browser window
66
+ rather than the display when the platform supports it, start recording before
67
+ normal user input, preserve the movie, and record the operating system and
68
+ recorder version. Confirm that the recorder process, not the browser or
69
+ simulator, owns capture and encoding. If native window capture is unavailable,
70
+ report the proof as blocked instead of substituting an in-page capture path.
71
+
72
+ The visual run contains only normal user input. Do not query the performance,
73
+ debug, or test bridge while it records. Treat that video as visual evidence of
74
+ frame progression. Collect worker paint, flush, delivery, and tier counters in
75
+ a separate run with screen capture off. The two runs answer different questions
76
+ without making the simulator measure its own recording overhead.
77
+
78
+ Open the sim with `?debug=framestamp` to make that video machine-readable.
79
+ Every surface paint then draws its `Date.now()` low 20 bits as a 23-cell
80
+ barcode — `home` and `app:*` at 45% of the surface height, `shell-overlay` on
81
+ its own row at 55% so overlay paints (keyboard, native UI) are attributable
82
+ separately. Decode the documented 23-cell barcode with a compatible framestamp
83
+ decoder to report, per presented frame, which paint it shows and how late that
84
+ paint presented. If no decoder is available, keep the recording as qualitative
85
+ evidence and make no numeric presentation-latency claim from it.
86
+
87
+ This is the only way to separate a frame the compositor never produced from one
88
+ it produced and the browser never presented.
89
+
90
+ **Screen capture is not presentation-neutral, so the decoded timing expires.**
91
+ Starting a ScreenCaptureKit window capture flips the 120hz `OffscreenCanvas`
92
+ out of latest-wins presentation into sequential presentation, which falls behind
93
+ roughly 0.5ms for every 1ms of capture. A capture started mid-activity begins
94
+ near zero latency and climbs from there. A recording is therefore truthful about
95
+ content and trajectory for its whole length, and truthful about timing for
96
+ roughly its first 500ms; latency growth past that is the capture, not the
97
+ engine. Live uncaptured presentation stays latest-wins and current. Never report
98
+ a late-recording latency as an engine number, and never compare two recordings
99
+ of different lengths.
100
+
101
+ Worker counters and presentation are different claims, and a green counter never
102
+ licenses a smoothness claim. A capture can report a perfect 120hz worker cadence
103
+ for motion the user never sees move: the overscroll release shipped exactly that
104
+ way, with the compositor stepping on schedule while the presented frames showed
105
+ a clamped fling instead of the bounce spring. Counters prove a tier engaged;
106
+ only a framestamp decode of an out-of-process recording proves what presented.
107
+
108
+ When an external transform control is smooth but the simulator is not, inspect
109
+ every animation on the presented element. A separate Web Animation object does
110
+ not isolate paint work: animating `box-shadow`, `filter`, or another paint-only
111
+ property on the transform owner can move the whole trajectory to the browser's
112
+ main thread. Paint stable decoration once or give it a distinct layer.
113
+
114
+ ```sh
115
+ rnx perf shell start # arm shell frame capture (clears prior)
116
+ # reproduce — e.g. 8 fling swipes:
117
+ # for i in {1..8}; do rnx do swipe 196 650 196 250 8 8 --no-wait; sleep 0.9; done
118
+ rnx perf shell stop # report (add --json for scripting)
119
+ ```
120
+
121
+ the report and how to read it:
122
+
123
+ - `painted frames / avg / p50 p95 p99 / jank` — work-per-painted-frame.
124
+ budget: 8ms at 120hz, 16.7ms at 60hz; p95 > 20ms is visible jank.
125
+ - `surfaces` — per-surface layout/render/flush split. `layout` spikes on a
126
+ scroll capture mean shell-side yoga re-layout when virtualized rows
127
+ materialize (mid-fling commit), not steady-state cost.
128
+ - `boundaries: N records / M replays` — layer compositor health. healthy
129
+ scroll: replays >> records, record cost near zero. records ≈ replays
130
+ means invalidation is defeating the cache (the SkPicture attempt-1
131
+ failure shape: `records: 1525, replays: 35`).
132
+ the `why recorded` line under it splits those records three ways, and each
133
+ one is a different bug: `invalidated` means something dirtied the boundary's
134
+ content (the usual culprit, and usually a style write that did not change a
135
+ value), `moved` means the boundary's record origin shifted so the cached
136
+ picture no longer lines up, and `first-record` is a boundary that had no
137
+ picture yet. a scroll dominated by `invalidated` is an invalidation bug; one
138
+ dominated by `first-record` is a warm-up problem, so look at whether the
139
+ idle pre-record reaches that content.
140
+ - `raster tier: promotions / blits` — blits per scrolled frame > 0 or the
141
+ tier is not firing; `raster skips` names the first gate each candidate
142
+ failed (rebuild / animated / cacheable). zero blits on a feed scroll is
143
+ a bug, not a tuning matter — but check you are reading the compositor's
144
+ block first (law 1), because the shell's is legitimately zero for an app
145
+ workload.
146
+ **before widening any skip bucket, read the `also fail a later gate`
147
+ line under it.** the counters name only the FIRST failed gate, so a big
148
+ bucket can be entirely blocked a second time and unwidenable: on one dense
149
+ token-list fling all 8298 `animated` skips also carried a style transform
150
+ or a non-cacheable chunk, and narrowing the animated gate moved 6007 to
151
+ `transform` and 2350 to `cacheable` while promoting nothing. a bucket is
152
+ worth attacking when its also-blocked share is small.
153
+ and blits firing is not the same as the tier paying for itself: on that same
154
+ token list the tier blits every visible row and costs 70MB of GPU
155
+ cache for no frame-time change, while the retained row boundaries under it
156
+ cost 2.4ms a frame. the discriminator is per-row draw density, so check
157
+ `draw calls` in the same block before concluding a cache is earning its
158
+ keep.
159
+ - `worst frames` — read the layout/render split of each; they are usually
160
+ a different problem than the average.
161
+
162
+ three runs minimum; single-run p95 is noise. a fresh `perf start` between
163
+ runs clears the buffer.
164
+
165
+ ### browser-site interaction matrices
166
+
167
+ for a browser-level regression, use three fresh documents per browser engine.
168
+ within each document, capture cold then warm versions of the same interaction;
169
+ do not call two independent fresh documents "cold versus warm." wait for the
170
+ home surface's first compositor paint before starting—the compositor-ready flag
171
+ only proves worker bootstrap and can precede the first visible layout.
172
+
173
+ during a measured transition, use a fixed observation window. do not poll the
174
+ shell state, test tree, or perf bridge while the animation is running; verify
175
+ the positive control once after profiling stops. query a targeted id or text on
176
+ large virtualized apps, never the full tree, because enumeration can dominate
177
+ or terminate the browser process. for a remotely populated list, require the
178
+ same scroll-container identity and content extent for a quiet window before the
179
+ gesture; "a row exists" can still be true while the app replaces the tree.
180
+ keep pointer down, moves, release, and the fixed settle window inside one
181
+ uninterrupted profile. stopping or querying between drag and release changes
182
+ velocity, cache state, and worker scheduling.
183
+
184
+ the minimum browser matrix is home drag plus snapback, a cold/warm built-in app
185
+ open, and a cold/warm app-switcher open. add one quiet heavy-app screen, one
186
+ navigation transition, and one long-list scroll when those surfaces are in
187
+ scope. use GPU-backed Chrome for Chromium claims. use installed WebKit in a
188
+ visible nonpersistent WKWebView for Safari-engine claims; Playwright WebKit is
189
+ not the installed Safari engine.
190
+
191
+ every receipt must carry the exact page and frame URLs, browser user agent,
192
+ browser executable path and content hash,
193
+ harness-source identity, capture time, engine build identity and generation,
194
+ runtime bundle receipt, sampled host capacity at run start, runtime errors, and
195
+ any partial failure. use the shared host-capacity sampler; do not record raw
196
+ `freemem()` or decide from macOS load average.
197
+ partial artifacts remain inadmissible until every requested run number for that
198
+ scenario exists with one harness, browser executable, engine, seed, and
199
+ viewport identity. browser
200
+ comparisons use the same root/target viewport tuple and backing scale. for a
201
+ wrapper or embedded surface, first prove the target has nonzero presented
202
+ geometry; a finite host viewport that responsively hides the target measures a
203
+ different surface. direct and embedded modes may intentionally have different
204
+ tuples, but browsers within one mode may not. finite but different device pixel
205
+ ratios are not comparable. if setup reloads only the child frame, replay the
206
+ parent's presentation control and wait for the new child to acknowledge it. a
207
+ selected parent toggle can outlive its child and is not proof of the child's
208
+ mode.
209
+ the profile intentionally blocks Graphite cache cleanup, so its cache snapshot
210
+ is not a terminal memory verdict. require a capture-local post-profile
211
+ compositor snapshot with before, after, and delta cleanup counters. an
212
+ over-limit profiled Context needs fresh submitted-work attempt and
213
+ successful-completion deltas; an over-limit profiled Recorder also needs a
214
+ fresh cleanup in that capture. classify Context and Recorder independently as
215
+ within-limit, locked-over-limit, or purgeable-over-limit; never compare their
216
+ aggregate, and never admit purgeable overage. a cache with zero purgeable bytes
217
+ can still hold a returned-resource queue: settled maintenance releases completed
218
+ Recorder resources, then budget enforcement evicts
219
+ least-recently-used overage before the next paint or resource insertion can
220
+ inherit the synchronous work. perpetual renderer
221
+ animation is not finite interaction demand; finite motion, readbacks, and
222
+ profiling must still block cleanup.
223
+ the host-clock `displayFrames`/`timerFrames` and shell-link
224
+ `displayPumps`/`timerPumps` counters prove whether the visible-rAF fallback
225
+ engaged. preserve failed receipts instead of retrying them out of the data set.
226
+
227
+ ### confirm which build you measured, every time
228
+
229
+ Against a dev-source sim, the FIRST reload after an engine edit serves the
230
+ PREVIOUS module. The sim reports ready, the app runs, the numbers look real, and
231
+ they describe the build before yours. Two screenshots were read as a fidelity
232
+ regression this way before the cause was found.
233
+
234
+ So make every capture say which build it came from, rather than assuming the
235
+ reload took. Put a marker in something the capture already returns - a counter
236
+ name in `saveLayerReasons`, a debug event, a log line - and treat a capture with
237
+ no marker as a capture of the wrong build. Reload twice and re-read the marker.
238
+
239
+ This costs one line and it is the difference between a measurement and a guess.
240
+ The same rule covers any A/B where you edit between arms: an arm you cannot
241
+ identify from its own output is not evidence.
242
+
243
+
244
+ deeper tools when the frame report isn't enough:
245
+
246
+ ```sh
247
+ rnx maestro test .maestro/scroll.yaml --profile # per-step + frame stats
248
+ rnx perf cpu --duration 5 --output /tmp/t.cpuprofile # sampled CPU
249
+ rnx open 8089 --new --driver playwright --cdp-port 9222 # cpu-profile a driveable sim
250
+ rnx debug enable layout,render && rnx debug recent layout 40
251
+ ```
252
+
253
+ `rnx perf cpu` writes one profile for the page and one for every attached
254
+ worker, plus a manifest that labels shell, compositor, tenant, worklet, and
255
+ unknown workers. exit zero requires page, shell, compositor, and tenant plus
256
+ every other discovered target, with each covering at least 99% of the requested
257
+ window. inspect start/stop offsets as well as sample counts; a late-attached
258
+ near-empty profile is incomplete. a profile of only the tenant—or a helper
259
+ that relabels the busiest worker as tenant—cannot distinguish CPU saturation
260
+ from sparse browser scheduling.
261
+
262
+ for an automated interaction, pass `--interaction-barrier` and start the cpu
263
+ command during the runner's pre-workload attachment delay, and give the runner
264
+ a post-workload delay long enough to remain open through profiler stop. the
265
+ pre-workload delay alone is not synchronization: it can end the profile while
266
+ the workload is still preparing. barrier mode pauses the workload before its
267
+ first measured capture until every discovered target is profiling, then rejects
268
+ the manifest unless the workload publishes its completion marker, every expected
269
+ capture label, and action timestamps inside the capture interval. retain its
270
+ browser executable, engine build and generation, root and target viewports, and
271
+ workload source identity in the CPU manifest. the barrier also has a bounded
272
+ page-side lease so an interrupted
273
+ profiler cannot strand the workload; lease expiry releases the runner but
274
+ invalidates the profile.
275
+
276
+ ### the cadence block: frames that never happened
277
+
278
+ work-per-painted-frame cannot see a frame that was never delivered because a
279
+ worker was descheduled or its resilient clock fell back late. the report shows
280
+ low avg and zero jank while the user sees stutter.
281
+ the `cadence` section of `perf shell stop` measures the delivery side and
282
+ attributes a missing frame to its hop:
283
+
284
+ - `display clock / host rAF interval` — stretch here (gaps >1.5x median)
285
+ means the page thread's rAF starved or was throttled, upstream of the
286
+ shell worker entirely.
287
+ - `delivery lag` — vsync postMessage receipt latency above the run's
288
+ best case; jitter here means the shell worker was descheduled or its
289
+ queue was busy.
290
+ - `paint interval` — the end-to-end cadence the user perceives.
291
+ - `idle breaks` — gaps >250ms, excluded from all three: demand-gated
292
+ quiesce is legitimate, not starvation.
293
+
294
+ the compositor `compositorHostFrameClock` keeps its historical name and reports
295
+ display/timer ticks consumed by the compositor worker's resilient clock during
296
+ the acknowledged profile window. `published` is null because renderer-main no
297
+ longer publishes compositor frames. sparse consumed ticks with low production
298
+ time identifies worker scheduling starvation. many consumed ticks with sparse
299
+ paints can be legitimate dirty-gating; correlate the frame series before
300
+ treating it as a regression. shell cadence comes from its worker frame and paint
301
+ series.
302
+
303
+ compare baseline vs contended captures of the same scripted window; also
304
+ compare **painted-frame counts**, the crudest and most robust signal.
305
+
306
+ the compositor cadence lines separate worker delivery from work submitted by
307
+ the worker:
308
+
309
+ - `engine-empty rAF` includes only sustained intervals where adjacent frames
310
+ have no engine demand, input message, shared-slot demand epoch change,
311
+ paint, prewarm pass, or CanvasKit flush.
312
+ - `rAF with CanvasKit flush` and `rAF after CanvasKit flush` isolate the frame
313
+ that called `Surface.flush()` and the next delivered frame.
314
+ - `CanvasKit submission` measures intervals between final `Surface.flush()`
315
+ completions on frames that submitted at least one surface. The JSON
316
+ `frameSeries` preserves each phase flag and exact count for correlation.
317
+
318
+ these are engine-owned boundaries. Playwright exposes raw CDP sessions only
319
+ for Chromium, and WebKit's public Inspector timeline exposes a WebCore
320
+ `Composite` record without a RemoteLayerTree transaction id or commit event.
321
+ Do not label CanvasKit flush or compositor rAF as a RemoteLayerTree commit.
322
+
323
+ ## the contention harness ("smooth alone, laggy on a zoom call")
324
+
325
+ low-load numbers do not predict feel under real-world CPU pressure. run the
326
+ same scripted capture twice, once with synthetic load, self-terminating so
327
+ it can't leak:
328
+
329
+ ```sh
330
+ # 6 cores of load for 30s, self-terminating
331
+ for i in 1 2 3 4 5 6; do
332
+ (perl -e 'my $end=time()+30; while(time()<$end){my $x=0; $x+=rand() for 1..10000}' &)
333
+ done
334
+ ```
335
+
336
+ compare: painted-frame count (cadence), avg/p95 (work), layout spikes. the
337
+ diagnostic split:
338
+
339
+ - **work grows under load** (avg/p95 way up) → paint path too heavy;
340
+ attack per-frame cost (layers, damage, raster, draw calls).
341
+ - **work flat but frames missing** (counts drop, avg barely moves) →
342
+ delivery starvation: the host-rAF → postMessage → shell hop chain and
343
+ worker scheduling. browsers give no thread priorities, so the only
344
+ levers are fewer hops on the frame-critical path, less work per hop, and
345
+ keeping motion (scroll, native anims) as close to the paint site as
346
+ possible.
347
+ - also remember GPU contention is real (video calls encode on the GPU);
348
+ webkit-lane numbers are Metal, chrome numbers are its GL/Metal stack.
349
+
350
+ ## the perf machinery map (improve, don't reinvent)
351
+
352
+ every tier below exists and has a counter. before optimizing, identify
353
+ which tier *should* absorb your cost and prove whether it does.
354
+
355
+ | tier | what it absorbs | proof it engaged |
356
+ | --- | --- | --- |
357
+ | demand-gated frames + visibility re-marking | idle costs nothing; invisible animations quiesce | `+N skipped idle ticks`; `queryStats().needsVsync` false at rest |
358
+ | per-surface dirty scoping | one app's commit/animation repaints only its surface | idle repaints ≈ commit rate, owning surface only |
359
+ | dirty-tracked layout + tiered text measurement | color/opacity/transform commits skip yoga; repeated strings skip shaping | `layout: 0` on steady frames |
360
+ | layer compositor (`paint-boundary.ts`) | animating a child never re-records ancestors | replays >> records |
361
+ | idle boundary pre-record | the first drag replays instead of recording; cold records happen while the surface is quiet | `pre-recorded: N boundaries at idle` in the report, and the first interaction's record cost near its warm cost |
362
+ | damage rects (`damage-rect.ts`) | repaint clips to changed region (opaque-backdrop gated) | worst-frame render bounded during small updates |
363
+ | raster tier | stable rows become GPU textures; scroll skips recording | compositor block: promotions > 0 once warm, blits ≈ one per visible row on a dense feed |
364
+ | flood guard, pointer coalescing, vsync hysteresis | message-storm and pump-thrash protection | msgs/s sane during gestures and at rest |
365
+ | worker-owned resilient frame clocks | renderer-main stalls do not stop shell or compositor motion | compositor consumed display/timer frames plus shell worker frame and paint cadence |
366
+
367
+ there is no whole-screen scroll blit. one existed for a few hours in June
368
+ 2026 and was reverted (`6706f3a014`): it shifted a surface RECTANGLE, so a
369
+ full-surface feed with floating translucent chrome dragged its bars along with
370
+ the content and smeared their old pixels through the feed. scroll rides the
371
+ layer, damage, and raster machinery above instead, and any future blit has to
372
+ own a scroll SUBTREE with provable backdrop correctness rather than a screen
373
+ rect. do not look for `blitBlocked`; it does not exist.
374
+
375
+ if a workload's cost lands in a tier that shows zero activity, the fix is
376
+ almost always "why didn't it engage" (a gate, a shape, an eligibility
377
+ rule), not new machinery. the area-gate fix that finally made the raster
378
+ tier fire on feeds came entirely from rejection counters.
379
+
380
+ ## guest-app-side causes (fix the app, or fix the engine honestly)
381
+
382
+ - dynamic format strings (`` `${n} messages` ``) bust the shaping LRU —
383
+ split number and label into separate `<Text>` nodes.
384
+ - `Animated.Value` on `width`/`height` forces layout per frame — use
385
+ `transform` (scale/translate).
386
+ - per-frame React work from reanimated-style code is an engine conformance
387
+ bug (valid worklet apps must not commit per frame) — fix rnx core,
388
+ never add a divergent fast-path (upstream alignment overrides perf; see
389
+ the `shouldFreezeOffscreenStackContent` incident).
390
+ - render-storm loops (poll → remount) show up as commit-rate repaints and
391
+ RSS growth; check `debug state animations` and the timeline.
392
+
393
+ ## closing the loop
394
+
395
+ done means: a named hot path + the counter or split that indicts it + the
396
+ same capture re-run showing the number moved + no new errors (`get errors`)
397
+ + nothing torn down left running (close sims you opened, kill load loops).
398
+ for engine changes, run the affected integration tests exposed by the installed
399
+ rnx version and capture stable screenshots of surfaces you touched.
@@ -0,0 +1,172 @@
1
+ ---
2
+ name: rnx-setup
3
+ description: Get rnx connected to your React Native app with no project setup
4
+ ---
5
+
6
+ # rnx setup
7
+
8
+ you want to drive a React Native app with rnx from the CLI. this skill
9
+ takes you from a running development server to a connected, pinned sim that
10
+ every other rnx skill assumes exists. rnx requires no project setup or per-app
11
+ install. if
12
+ `rnx describe` already
13
+ returns a tree, you're past this skill — load `/rnx-debug` for
14
+ debugging or `/rnx-test` for writing tests.
15
+
16
+ ## anti-patterns (read first)
17
+
18
+ - **never `rnx claim --force` a sim held by another CLI.** the lease
19
+ exists for a reason. forcing strands the other agent and corrupts the
20
+ bridge state. use `rnx open --new` to start a fresh sim instead.
21
+ - **don't switch sims mid-investigation.** pin once with `rnx use
22
+ <id>` and keep it pinned. an unpinned command refuses to choose when more
23
+ than one driveable sim is live.
24
+ - **an empty `rnx list` is not "broken rnx".** it usually means the
25
+ daemon is down, the dev server port is wrong, or the runtime is missing.
26
+ walk the recovery checklist before reinstalling.
27
+ - **keep one sim for your whole session.** the first `rnx open <port>`
28
+ launches Chrome for Testing with an isolated profile. later opens reuse the
29
+ saved sim and navigate it in place. use `rnx do reload` when the target
30
+ has not changed:
31
+
32
+ ```sh
33
+ rnx open 8086 # first call creates your isolated sim
34
+ rnx open 8090 # same sim, different app
35
+ rnx do reload # same sim, same app
36
+ rnx close <id> # dispose it when you are done
37
+ ```
38
+
39
+ `--new`, `--profile`, and `--ephemeral` create another isolated browser
40
+ tree. use them only when separate storage or a genuinely concurrent sim is
41
+ required. repeated new trees consume enough memory and CPU to starve other
42
+ work on a shared machine.
43
+ - **pin the sim before driving it.** `rnx open` records the new sim for the
44
+ current CLI identity. read its id and pin it explicitly before a longer flow:
45
+
46
+ ```sh
47
+ rnx open 8086
48
+ rnx list
49
+ rnx use <id>
50
+ rnx describe --sim <id>
51
+ ```
52
+
53
+ - **never retry-loop `rnx open` against a wedged or slow stack.** a
54
+ connect timeout means diagnose the stack. do not retry. if the first launch
55
+ never connects, each retry can create another browser tree before there is a
56
+ saved sim to reuse. verify the development server and rnx runtime, then
57
+ retry once after fixing the cause.
58
+
59
+ ## first move
60
+
61
+ ```sh
62
+ rnx daemon status # the persistent service makes agent commands much faster
63
+ rnx daemon install # enable it when missing on a supported personal machine
64
+ rnx compat --json # scan the app's native package compatibility
65
+ rnx open 8081 # load a metro/expo dev server
66
+ rnx list # confirm exactly one sim is reachable
67
+ rnx use <id> # pin it for the rest of the workflow
68
+ rnx describe # smoke test — should print a render tree
69
+ ```
70
+
71
+ the background service keeps the local bridge and runtime ready between
72
+ commands. without it, rnx still works by starting an on-demand bridge, but
73
+ inspect, interaction, and test commands pay repeated startup cost. if the user
74
+ declined it during onboarding, `rnx daemon install` is the direct way to enable
75
+ it. CI does not need or install the service.
76
+
77
+ bare `rnx` opens ConnectRN for a human to choose among local apps. agents and
78
+ scripts should use `rnx open <port-or-url>` so the target is explicit.
79
+
80
+ if you do not know the port, run `rnx open` with no target and pick from
81
+ the detected dev servers. for hosted or unusual targets, pass the URL
82
+ explicitly:
83
+
84
+ ```sh
85
+ rnx open 8081 # load a metro dev server
86
+ rnx open https://my-app.local # load a hosted bundle
87
+ ```
88
+
89
+ ## you're done when
90
+
91
+ - `rnx list` shows your sim and no orphans
92
+ - `rnx describe` returns a render tree, not an error
93
+ - the same sim id appears in `rnx describe`, `rnx find`, and
94
+ every subsequent command
95
+ - `rnx get errors 5` and `rnx get requests 5` are quiet (or the
96
+ warnings are ones you understand)
97
+ - the compatibility scan's partial, unsupported, unknown, and version-mismatched
98
+ packages have been summarized without treating its aggregate score as a pass/fail gate
99
+
100
+ ## compatibility feedback requires approval
101
+
102
+ run `rnx compat --json` from the app project root during setup. if the scan
103
+ identifies a plausible missing rnx seam, or the running app gives concrete
104
+ evidence that behavior is missing or broken specifically in rnx, summarize
105
+ the package, expected behavior, actual behavior, and runtime evidence for the
106
+ user. then offer to send that finding with `rnx report-issue`.
107
+
108
+ **never submit a report automatically or behind the user's back.** wait for an
109
+ explicit yes. only then run this from the app project root:
110
+
111
+ ```sh
112
+ rnx report-issue --yes "<package; expected behavior; actual behavior; runtime evidence>"
113
+ ```
114
+
115
+ the command automatically attaches the local compatibility scan plus bounded
116
+ CLI, runtime, operating-system, and architecture metadata. it does not attach
117
+ source files, environment variables, terminal output, logs, screenshots, git
118
+ data, or app data. `--dry-run` previews the attachment summary without sending.
119
+ do not use `--yes` merely because the agent believes a report would be useful;
120
+ it records the user's approval after the offer.
121
+
122
+ ## recovery — common failure modes
123
+
124
+ **`rnx list` is empty.** walk this checklist in order:
125
+
126
+ ```sh
127
+ rnx daemon status # is the bridge daemon up?
128
+ rnx runtime list # is at least one engine runtime installed?
129
+ rnx open <port> # does loading explicitly work?
130
+ ```
131
+
132
+ if the daemon is not installed: run `rnx daemon install`. if the registered
133
+ daemon is down, run `rnx daemon restart`, or `rnx serve` in another shell for
134
+ a foreground bridge. if no runtime is installed, `rnx open` installs it
135
+ before launching. if open errors with "port unreachable": confirm your dev
136
+ server is actually serving — the bundler has to be up before rnx can
137
+ attach.
138
+
139
+ **two sims appear unexpectedly.** an earlier `rnx claim` didn't
140
+ release on exit. close the orphan explicitly:
141
+
142
+ ```sh
143
+ rnx list # note the stale id
144
+ rnx close <id> # release it cleanly
145
+ ```
146
+
147
+ **`rnx open` errors with "no runtime".** the engine binary isn't installed.
148
+ Run `rnx runtime install`, then retry `rnx open <port>`.
149
+
150
+ **`rnx open` times out ("timed out waiting for opened sim to connect").**
151
+ do not retry in a loop. verify the development server first: `__soot/` must
152
+ respond and the engine watchers must be running. inspect the driver diagnostic
153
+ path printed by the failed command. restart the broken development stack at its
154
+ supervisor, then retry once. rnx owns the isolated browser profile and its
155
+ process tree; do not open the shell URL through an operating-system browser.
156
+
157
+ **bridge disconnects mid-sim.** the WebSocket dropped (laptop slept,
158
+ network blip). re-pin: `rnx use <id>`. the daemon retries reconnection
159
+ automatically; you usually just need to re-issue the command.
160
+
161
+ **"sim held by another CLI".** another agent or your own previous CLI
162
+ process has the lease. find it (`rnx list`), close it (`rnx close
163
+ <id>`), or start a clean sim (`rnx open --new`) — do **not**
164
+ `rnx claim --force`.
165
+
166
+ ## related
167
+
168
+ - `/rnx-debug` — once setup is good, this is where you debug
169
+ rendering, performance, and accessibility issues.
170
+ - `/rnx-test` — write or run automated flows against the connected
171
+ sim.
172
+ - `/rnx-visual` — pixel-level rendering comparisons.