[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-vercel-labs-automation":3,"mdc-w6nuy2-key":32,"related-repo-vercel-labs-automation":2957,"related-org-vercel-labs-automation":3017},{"slug":4,"name":4,"fn":5,"description":6,"org":7,"tags":11,"stars":21,"repoUrl":22,"updatedAt":23,"license":24,"forks":25,"topics":26,"repo":27,"sourceUrl":30,"mdContent":31},"automation","automate and verify Native SDK applications","Automation and verification guide for running Native SDK apps. Use when the user asks to test a running app, inspect runtime state, list windows, wait for readiness, drive widgets, take deterministic screenshots, send bridge commands, debug why automation is not connected, create smoke tests, or verify a Native SDK example in a GUI-capable session.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},"vercel-labs","Vercel Labs","https:\u002F\u002Fpexgzepcugksgbtrxkhf.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Forg-logos\u002Fvercel-labs.png",[12,15,18],{"name":13,"slug":4,"type":14},"Automation","tag",{"name":16,"slug":17,"type":14},"Mobile","mobile",{"name":19,"slug":20,"type":14},"Testing","testing",6016,"https:\u002F\u002Fgithub.com\u002Fvercel-labs\u002Fnative","2026-07-20T05:57:16.544049",null,244,[],{"repoUrl":22,"stars":21,"forks":25,"topics":28,"description":29},[],"Toolkit for building native desktop apps","https:\u002F\u002Fgithub.com\u002Fvercel-labs\u002Fnative\u002Ftree\u002FHEAD\u002Fskill-data\u002Fautomation","---\nname: automation\ndescription: Automation and verification guide for running Native SDK apps. Use when the user asks to test a running app, inspect runtime state, list windows, wait for readiness, drive widgets, take deterministic screenshots, send bridge commands, debug why automation is not connected, create smoke tests, or verify a Native SDK example in a GUI-capable session.\n---\n\n# Automate Native SDK apps\n\nEvery Native SDK app embeds an automation server — native-rendered apps and WebView-shell apps alike. It works through file-based IPC in `.zig-cache\u002Fnative-sdk-automation\u002F` and is intended for smoke tests, CI checks with a GUI session, and quick runtime inspection: accessibility snapshots, widget driving through the real input paths, deterministic reference-renderer screenshots, readiness\u002Fstate assertions, and bridge round-trips.\n\nAutomation is not browser DOM automation. It reports runtime\u002Fwindow\u002Fwidget state, drives retained-canvas widgets, and can ask the runtime to reload or dispatch bridge requests. For DOM testing of the optional WebView path, use the frontend framework's tests or a browser automation tool against the dev server.\n\n## What automation can verify\n\n- An automation-enabled app started and published `ready=true`.\n- The runtime loaded the expected app name, source kind, and window metadata.\n- The main window exists and is focused\u002Fopen.\n- The JavaScript-to-Zig bridge can round-trip a request through `native automate bridge`.\n- Builtin window\u002FWebView commands work when exercised by a smoke test.\n- Reload requests are accepted by the runtime.\n- Real pixels of retained-canvas (`gpu_surface`) views: `native automate screenshot \u003Cview-label>` renders the view's current canvas frame through the deterministic CPU reference renderer and writes a PNG artifact. Two captures of an unchanged scene are byte-identical, so screenshots can back golden-image or \"did the UI change\" checks.\n\n## What automation cannot verify\n\n- Screenshots of WebView content. `screenshot` covers `gpu_surface` canvas views only; there is no DOM\u002FWebView pixel capture.\n- Arbitrary DOM queries and clicks.\n- Browser network assertions.\n\n## Prerequisites\n\nBuild\u002Frun an app with automation enabled. Generated examples usually expose `-Dautomation=true`:\n\n```bash\nzig build run -Dplatform=macos -Dautomation=true\n```\n\nRepository examples may have specialized steps:\n\n```bash\nzig build run-webview -Dplatform=macos -Dautomation=true\nzig build test-webview-smoke -Dplatform=macos\n```\n\nThe runner must pass an automation server into `RuntimeOptions`:\n\n```zig\nconst server = native_sdk.automation.Server.init(io, \".zig-cache\u002Fnative-sdk-automation\", \"My App\");\nvar runtime = native_sdk.Runtime.init(.{\n    .platform = my_platform,\n    .automation = server,\n});\n```\n\nApps built without `-Dautomation=true` usually ignore automation files.\n\n## Commands\n\n```bash\nnative automate wait\nnative automate assert 'gpu_nonblank=true' 'role=button name=\"Reset\"'\nnative automate assert --absent 'error event='\nnative automate list\nnative automate snapshot\nnative automate reload\nnative automate screenshot inbox-canvas\nnative automate screenshot inbox-canvas 2\nnative automate widget-action canvas 2 press\nnative automate widget-click canvas 3\nnative automate widget-hold canvas 3\nnative automate widget-context-press canvas 3\nnative automate widget-drag canvas 4 0.25 0.82\nnative automate widget-wheel canvas 5 18\nnative automate widget-key canvas tab\nnative automate widget-key canvas cmd+c\nnative automate widget-pinch canvas 1.5\nnative automate widget-pinch canvas 0.5 120 80\nnative automate profile on\nnative automate profile off\nnative automate bridge '{\"id\":\"smoke\",\"command\":\"native.ping\",\"payload\":{\"source\":\"automation\"}}'\n```\n\nIf using the repository-built CLI:\n\n```bash\nzig-out\u002Fbin\u002Fnative automate wait\nzig-out\u002Fbin\u002Fnative automate snapshot\n```\n\n## Snapshot assertions (`automate assert`)\n\nPrefer `native automate assert` over `snapshot | grep` chains: it polls, so no sleeps, and its failure output carries the evidence (each missing pattern plus the snapshot tail).\n\n```bash\nnative automate assert 'gpu_nonblank=true' 'role=button name=\"Reset\"' 'count: 0'\nnative automate assert --timeout-ms 10000 '4 open'\nnative automate assert --absent 'error event=' 'dispatch_errors=[1-9]'\n```\n\nSemantics:\n\n- Every argument is a regex that must match somewhere in `snapshot.txt`. The command polls (100ms interval) until all match, then exits 0.\n- `--timeout-ms \u003Cn>` bounds the polling (default 30000). On timeout it prints `missing: \u003Cpattern>` for each unmatched pattern, the last 20 snapshot lines, and exits non-zero — CI-friendly, no wrapper script needed.\n- `--absent` inverts the whole invocation: every pattern must NOT match (poll until gone). Mix presence and absence by running two invocations.\n- Supported regex subset: literals, `.`, postfix `*` `+` `?`, line anchors `^`\u002F`$`, classes `[a-z]`\u002F`[^0-9]`, and `\\d \\w \\s` (with uppercase negations). No groups or alternation — pass multiple patterns instead.\n- Quote patterns in single quotes so the shell leaves `\"`, `$`, and `\\d` alone.\n\n## Standard workflow\n\n1. Start the app with automation enabled.\n2. Run `native automate wait` to block until `snapshot.txt` contains `ready=true`.\n3. Run `native automate assert '\u003Cpattern>' ...` for state checks, or `native automate snapshot` to eyeball app\u002Fwindow\u002Fsource metadata.\n4. Run `native automate list` to inspect window summaries.\n5. Run `native automate bridge '...'` for bridge round-trip checks.\n6. Use `native automate widget-action \u003Cview-label> \u003Cwidget-id> \u003Caction> [value]` to exercise retained canvas widget actions. `set_text` routes through the SAME input path real typing uses (focus, select-all, then a text-input event), so a TEA app's `on_input` mirror receives the edits and model state stays consistent with the on-screen field — it is not a presentation-only write.\n7. Use `native automate widget-click \u003Cview-label> \u003Cwidget-id>` to exercise pointer-style retained widget routing. `widget-hold \u003Cview-label> \u003Cwidget-id>` drives a press-and-hold through the same path — pointer down, the reserved hold timer fired, then the suppressed release — so `on_hold` Msgs are live-drivable (a target without `on_hold` degrades to the click a real long press is). `widget-context-press \u003Cview-label> \u003Cwidget-id>` is the secondary click: it presents the widget's context menu, or dispatches `on_hold` immediately when the route declares none.\n8. Use `native automate widget-drag \u003Cview-label> \u003Cwidget-id> \u003Cstart-x-ratio> \u003Cend-x-ratio> [start-y-ratio end-y-ratio]` for continuous pointer controls.\n9. Use `native automate widget-wheel \u003Cview-label> \u003Cwidget-id> \u003Cdelta-y>` for retained widget scroll input. Wheel targets must be interactive\u002Fscrollable widgets — a plain layout column or text node is not a wheel target; aim at the scroll\u002Flist widget id from the snapshot. Failures land in the snapshot as named reasons: `error event=automation.widget_wheel name=WheelTargetUnknown|WheelTargetNotInteractive|WheelTargetHasEmptyBounds detail=\"\u003Ccommand args>\"`.\n10. Use `native automate widget-key \u003Cview-label> \u003Ckey> [text]` for focused retained widget keyboard input. The key accepts modifier chords — `cmd+a`, `cmd+c`, `cmd+v`, `cmd+x`, `ctrl+shift+arrowleft` (`cmd` sets the primary shortcut modifier on every platform) — so select-all\u002Fcopy\u002Fcut\u002Fpaste and shift-extended selection are drivable; after a copy, widget lines in the snapshot show the live selection as `selection=a..b`, and the copied text lands on the real system clipboard (`pbpaste` on macOS).\n11. Use `native automate widget-pinch \u003Cview-label> \u003Cscale> [x y]` for trackpad pinch gestures against a gpu-surface view: the runtime dispatches the real `pinch_begin`\u002F`pinch_change`\u002F`pinch_end` platform events, with one change carrying `scale - 1`. `\u003Cscale>` is the FINAL multiplicative zoom for the gesture — the cumulative gesture scale (the product of `1 + delta`) lands exactly on it — `1.5` zooms in 50%, `0.5` zooms out to half. The optional anchor point is view-local points, defaulting to the view center. Apps hear it through the pinch channel (`Options.on_pinch` \u002F the TS core's `pinchMsg`).\n12. Use `native automate screenshot \u003Cview-label> [scale]` to capture the named `gpu_surface` view's canvas as `screenshot-\u003Cview-label>.png` (the CLI prints the artifact path and waits for the file).\n13. Use `native automate tray-action \u003Citem-id>` to select a status-item dropdown row through the same platform event a real menu-bar click emits (command dispatch with source `.tray`). The live tray is visible in `snapshot.txt` as a `tray title=\"...\" items=N` line followed by `  tray-item #id label=\"...\" command=\"...\" enabled=...` rows — the macOS menu bar is outside every window capture, so the snapshot is the only automation evidence the model-driven tray exists, and the `#id` there is what `tray-action` takes. Unknown ids degrade into the dispatch-error ring as `automation.tray_action`.\n14. Use `native automate reload` to request a WebView reload.\n15. Use `native automate profile on` to enable per-stage frame timing: while on, `snapshot.txt` carries a `frame_profile` line with rolling p50\u002Fp90\u002Fmax microseconds per pipeline stage (`rebuild`, `layout`, `reconcile`, `emit`, `a11y`, `plan`, `patch`, `encode`, `present`, `host_decode`, `host_draw`), each with a lifetime sample count (`\u003Cstage>_n=`). Drive some interactions, then `native automate snapshot | grep -o 'frame_profile.*'` to read where frame time goes; `profile off` stops recording and drops the line. Turning it on starts a fresh sample window.\n\n## Screenshots\n\n`screenshot \u003Cview-label> [scale]` asks the runtime to rasterize the view's\ncurrent retained canvas frame through the deterministic CPU reference\nrenderer — the same pixel path the Linux software presentation uses — and\npublish it as an uncompressed PNG at\n`.zig-cache\u002Fnative-sdk-automation\u002Fscreenshot-\u003Cview-label>.png`. The file is\nwritten atomically (temp file + rename), so its presence means the PNG is\ncomplete.\n\nDeterminism semantics:\n\n- Screenshots render at scale 1 by default regardless of the display's\n  backing scale, so an unchanged scene produces byte-identical PNGs from\n  capture to capture on the same machine. Pass an explicit scale (for\n  example `2`) for high-DPI pixel dimensions.\n- Screenshots use the live retained scene, including live design tokens and\n  platform text measurement (CoreText on macOS): the layout matches what is\n  on screen. Glyphs are rasterized by the reference renderer from the\n  bundled faces, not by the platform's font rasterizer, so screenshots are a\n  deterministic layout\u002Fstructure\u002Fcolor signal rather than a platform\n  font-rendering signal.\n- Cross-machine byte-identity is only guaranteed where text metrics are\n  deterministic (the null platform's estimator). On platforms with a native\n  text measurement provider, text widths can differ between OS versions, so\n  compare screenshots taken on the same machine or assert on properties\n  (dimensions, changed\u002Funchanged) rather than exact bytes across machines.\n- OS-level captures (`screencapture -x` on macOS) are NOT a substitute: in a\n  shell without Screen Recording permission they exit 0 and silently return\n  wallpaper-only images with no app window in them. If you shell out for a\n  real-pixel capture, verify the image is not blank\u002Fwallpaper before trusting\n  it; `automate screenshot` plus a semantics snapshot is the reliable pair.\n\n## Bridge smoke test pattern\n\nThe request must be JSON with an ID, command, and payload:\n\n```bash\nnative automate bridge '{\"id\":\"smoke\",\"command\":\"native.ping\",\"payload\":{\"source\":\"automation\"}}'\n```\n\nAutomation sends the request with origin `zero:\u002F\u002Finline`. The app's bridge policy must allow that origin or the call will reject with `permission_denied`. For packaged asset origins, app code often allows `zero:\u002F\u002Fapp`; for automation smoke tests, add `zero:\u002F\u002Finline` only when the test needs it.\n\nExpected response shape depends on the handler. A typical `native.ping` handler returns:\n\n```json\n{\"id\":\"smoke\",\"ok\":true,\"result\":{\"message\":\"pong\",\"count\":1}}\n```\n\nIf the command fails, inspect the bridge error code:\n\n- `unknown_command`: no handler registered or wrong command name.\n- `permission_denied`: origin or permission policy blocked it.\n- `handler_failed`: Zig handler returned an error or invalid JSON.\n- `payload_too_large`: request exceeded bridge limits.\n\n## File protocol\n\nThe default directory is `.zig-cache\u002Fnative-sdk-automation\u002F`, resolved against the CLI's CURRENT WORKING DIRECTORY — run `native automate` from the app project's directory (where the app was launched). The dir is created by the running app, never by the CLI: a command sent from the wrong cwd fails loudly (`error: no automation dir at \u003Cabs path>`) instead of queueing into a dir no app reads, and every queued command prints the absolute dir it wrote to — check that line when a command seems to do nothing.\n\nFiles:\n\n- `snapshot.txt`: app name, readiness, source kind, source size, window metadata, accessibility summary. The `ready=true` line also carries `protocol=\u003Cn>` (the CLI\u002Fapp handshake: the CLI refuses snapshots — and command queues to a live app — whose protocol version is not its own, naming both versions; the fix is rebuilding whichever binary is stale and comparing `native version`), `dispatch_errors=\u003Ctotal>` and `dropped_trace_records=\u003Ctotal>`, and recent degraded handler\u002Fupdate errors appear as `  error event=\u003Ctag> name=\u003CErrorName> timestamp_ns=...` lines — a handler error no longer exits the app, so grep these to notice one happened. While `profile on` is active, a `frame_profile \u003Cstage>_p50_us=... \u003Cstage>_p90_us=... \u003Cstage>_max_us=... \u003Cstage>_n=...` line follows the header with per-stage frame timing. The header also carries `markup_watch=armed|off` — whether the markup hot-reload watch is armed (armed only in builds where the app wired `.markup` with a `watch_path` and `io`, or registered compiled fragments through `fragment_watch` — i.e. Debug dev builds).\n- `windows.txt`: window list.\n- `command.txt`: command input written by CLI and consumed by runtime. The slot is single-entry and the app consumes one command per presented frame: `native automate \u003Ccommand>` WAITS for the running app to consume it and prints `delivered \u003Caction> -> \u003Cdir>` on success — it refuses\u002Ffails loudly instead of silently overwriting an unconsumed command, and exits non-zero if the app never consumes it.\n- `bridge-response.txt`: last bridge response.\n- `screenshot-\u003Cview-label>.png`: deterministic reference-rendered PNG of a `gpu_surface` view, written by the `screenshot` command.\n\nThe runtime polls `command.txt`. After processing a command, it writes `done`.\n\n## Debugging automation failures\n\nIf `native automate wait` times out with NO snapshot file at all, it prints a teaching error naming the automation dir it watched and pointing at `-Dautomation=true` and the working directory — start from that message. Otherwise:\n\n1. Confirm the app is still running.\n2. Confirm it was built with `-Dautomation=true`.\n3. Confirm the runner passes `automation` into `Runtime.init`.\n4. Check `.zig-cache\u002Fnative-sdk-automation\u002Fsnapshot.txt`.\n5. Delete stale files in `.zig-cache\u002Fnative-sdk-automation\u002F` and restart the app.\n6. Run with more tracing, for example `zig build run -Dtrace=all`.\n\nIf the CLI reports an automation protocol mismatch (or a snapshot with no protocol version): the `native` binary and the app were built from different framework versions — rebuild the older side (stale `zig-out\u002Fbin` copies of the CLI are the classic cause; `native version` prints the commit the binary was built from).\n\nStale INSTANCES are called out too: automate verbs print a LOUD warning when the publishing app's process started BEFORE the newest binary in `zig-out\u002Fbin` was built — a leftover instance from an earlier run impersonating the new build. Kill it and relaunch the fresh binary before trusting any snapshot.\n\nIf `snapshot` says no app connected:\n\n- The automation directory may not exist yet.\n- The app may be running from a different working directory.\n- The app may be built without automation.\n- The app may not have reached runtime startup.\n\nIf bridge automation fails:\n\n- Check command name spelling.\n- Check app handler registration.\n- Check bridge policy origins for `zero:\u002F\u002Finline`.\n- Check runtime permissions.\n- Check that the handler returns valid JSON.\n\n## CI and smoke tests\n\nUse automation for minimal integration confidence:\n\n```bash\nzig build test-webview-smoke -Dplatform=macos\n```\n\nA good smoke test:\n\n1. Builds an example with `-Dautomation=true` and `-Djs-bridge=true`.\n2. Starts the app in a GUI-capable session.\n3. Waits for readiness.\n4. Verifies snapshot metadata (`automate assert` with the patterns that matter).\n5. Sends `native.ping`.\n6. Exercises builtin windows\u002FWebViews if the app enables them.\n7. Fails on timeout or unexpected bridge response.\n\nApps scaffolded with `native init --full` ship this as `.github\u002Fworkflows\u002Fci.yml` (the zero-config default scaffold skips CI): a null-platform `zig build test` job plus a Linux Xvfb smoke job that launches the binary, runs `automate wait`, asserts on the snapshot with `automate assert`, and checks a non-empty `automate screenshot` artifact. Extend that file rather than writing grep chains by hand.\n\nDo not use automation for exhaustive UI testing. It is a runtime and bridge smoke layer.\n\n## Notes\n\n- Automation is compile-time gated: apps built without `-Dautomation=true` ignore automation files.\n- Screenshots cover retained-canvas (`gpu_surface`) views only; WebView pixels are not captured.\n- WebView DOM interaction is intentionally out of scope for this file-based automation layer.\n- Use `native skills get core --full` for app architecture, bridge policy, packaging, and debugging context.\n",{"data":33,"body":34},{"name":4,"description":6},{"type":35,"children":36},"root",[37,46,61,66,73,144,150,183,189,202,246,251,304,316,367,379,385,947,952,990,1004,1025,1166,1171,1318,1324,1909,1915,1934,1939,1986,1992,1997,2031,2067,2080,2230,2235,2281,2287,2315,2320,2508,2527,2533,2552,2626,2653,2665,2677,2700,2705,2739,2745,2750,2776,2781,2845,2896,2901,2907,2951],{"type":38,"tag":39,"props":40,"children":42},"element","h1",{"id":41},"automate-native-sdk-apps",[43],{"type":44,"value":45},"text","Automate Native SDK apps",{"type":38,"tag":47,"props":48,"children":49},"p",{},[50,52,59],{"type":44,"value":51},"Every Native SDK app embeds an automation server — native-rendered apps and WebView-shell apps alike. It works through file-based IPC in ",{"type":38,"tag":53,"props":54,"children":56},"code",{"className":55},[],[57],{"type":44,"value":58},".zig-cache\u002Fnative-sdk-automation\u002F",{"type":44,"value":60}," and is intended for smoke tests, CI checks with a GUI session, and quick runtime inspection: accessibility snapshots, widget driving through the real input paths, deterministic reference-renderer screenshots, readiness\u002Fstate assertions, and bridge round-trips.",{"type":38,"tag":47,"props":62,"children":63},{},[64],{"type":44,"value":65},"Automation is not browser DOM automation. It reports runtime\u002Fwindow\u002Fwidget state, drives retained-canvas widgets, and can ask the runtime to reload or dispatch bridge requests. For DOM testing of the optional WebView path, use the frontend framework's tests or a browser automation tool against the dev server.",{"type":38,"tag":67,"props":68,"children":70},"h2",{"id":69},"what-automation-can-verify",[71],{"type":44,"value":72},"What automation can verify",{"type":38,"tag":74,"props":75,"children":76},"ul",{},[77,91,96,101,113,118,123],{"type":38,"tag":78,"props":79,"children":80},"li",{},[81,83,89],{"type":44,"value":82},"An automation-enabled app started and published ",{"type":38,"tag":53,"props":84,"children":86},{"className":85},[],[87],{"type":44,"value":88},"ready=true",{"type":44,"value":90},".",{"type":38,"tag":78,"props":92,"children":93},{},[94],{"type":44,"value":95},"The runtime loaded the expected app name, source kind, and window metadata.",{"type":38,"tag":78,"props":97,"children":98},{},[99],{"type":44,"value":100},"The main window exists and is focused\u002Fopen.",{"type":38,"tag":78,"props":102,"children":103},{},[104,106,112],{"type":44,"value":105},"The JavaScript-to-Zig bridge can round-trip a request through ",{"type":38,"tag":53,"props":107,"children":109},{"className":108},[],[110],{"type":44,"value":111},"native automate bridge",{"type":44,"value":90},{"type":38,"tag":78,"props":114,"children":115},{},[116],{"type":44,"value":117},"Builtin window\u002FWebView commands work when exercised by a smoke test.",{"type":38,"tag":78,"props":119,"children":120},{},[121],{"type":44,"value":122},"Reload requests are accepted by the runtime.",{"type":38,"tag":78,"props":124,"children":125},{},[126,128,134,136,142],{"type":44,"value":127},"Real pixels of retained-canvas (",{"type":38,"tag":53,"props":129,"children":131},{"className":130},[],[132],{"type":44,"value":133},"gpu_surface",{"type":44,"value":135},") views: ",{"type":38,"tag":53,"props":137,"children":139},{"className":138},[],[140],{"type":44,"value":141},"native automate screenshot \u003Cview-label>",{"type":44,"value":143}," renders the view's current canvas frame through the deterministic CPU reference renderer and writes a PNG artifact. Two captures of an unchanged scene are byte-identical, so screenshots can back golden-image or \"did the UI change\" checks.",{"type":38,"tag":67,"props":145,"children":147},{"id":146},"what-automation-cannot-verify",[148],{"type":44,"value":149},"What automation cannot verify",{"type":38,"tag":74,"props":151,"children":152},{},[153,173,178],{"type":38,"tag":78,"props":154,"children":155},{},[156,158,164,166,171],{"type":44,"value":157},"Screenshots of WebView content. ",{"type":38,"tag":53,"props":159,"children":161},{"className":160},[],[162],{"type":44,"value":163},"screenshot",{"type":44,"value":165}," covers ",{"type":38,"tag":53,"props":167,"children":169},{"className":168},[],[170],{"type":44,"value":133},{"type":44,"value":172}," canvas views only; there is no DOM\u002FWebView pixel capture.",{"type":38,"tag":78,"props":174,"children":175},{},[176],{"type":44,"value":177},"Arbitrary DOM queries and clicks.",{"type":38,"tag":78,"props":179,"children":180},{},[181],{"type":44,"value":182},"Browser network assertions.",{"type":38,"tag":67,"props":184,"children":186},{"id":185},"prerequisites",[187],{"type":44,"value":188},"Prerequisites",{"type":38,"tag":47,"props":190,"children":191},{},[192,194,200],{"type":44,"value":193},"Build\u002Frun an app with automation enabled. Generated examples usually expose ",{"type":38,"tag":53,"props":195,"children":197},{"className":196},[],[198],{"type":44,"value":199},"-Dautomation=true",{"type":44,"value":201},":",{"type":38,"tag":203,"props":204,"children":209},"pre",{"className":205,"code":206,"language":207,"meta":208,"style":208},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","zig build run -Dplatform=macos -Dautomation=true\n","bash","",[210],{"type":38,"tag":53,"props":211,"children":212},{"__ignoreMap":208},[213],{"type":38,"tag":214,"props":215,"children":218},"span",{"class":216,"line":217},"line",1,[219,225,231,236,241],{"type":38,"tag":214,"props":220,"children":222},{"style":221},"--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B",[223],{"type":44,"value":224},"zig",{"type":38,"tag":214,"props":226,"children":228},{"style":227},"--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D",[229],{"type":44,"value":230}," build",{"type":38,"tag":214,"props":232,"children":233},{"style":227},[234],{"type":44,"value":235}," run",{"type":38,"tag":214,"props":237,"children":238},{"style":227},[239],{"type":44,"value":240}," -Dplatform=macos",{"type":38,"tag":214,"props":242,"children":243},{"style":227},[244],{"type":44,"value":245}," -Dautomation=true\n",{"type":38,"tag":47,"props":247,"children":248},{},[249],{"type":44,"value":250},"Repository examples may have specialized steps:",{"type":38,"tag":203,"props":252,"children":254},{"className":205,"code":253,"language":207,"meta":208,"style":208},"zig build run-webview -Dplatform=macos -Dautomation=true\nzig build test-webview-smoke -Dplatform=macos\n",[255],{"type":38,"tag":53,"props":256,"children":257},{"__ignoreMap":208},[258,282],{"type":38,"tag":214,"props":259,"children":260},{"class":216,"line":217},[261,265,269,274,278],{"type":38,"tag":214,"props":262,"children":263},{"style":221},[264],{"type":44,"value":224},{"type":38,"tag":214,"props":266,"children":267},{"style":227},[268],{"type":44,"value":230},{"type":38,"tag":214,"props":270,"children":271},{"style":227},[272],{"type":44,"value":273}," run-webview",{"type":38,"tag":214,"props":275,"children":276},{"style":227},[277],{"type":44,"value":240},{"type":38,"tag":214,"props":279,"children":280},{"style":227},[281],{"type":44,"value":245},{"type":38,"tag":214,"props":283,"children":285},{"class":216,"line":284},2,[286,290,294,299],{"type":38,"tag":214,"props":287,"children":288},{"style":221},[289],{"type":44,"value":224},{"type":38,"tag":214,"props":291,"children":292},{"style":227},[293],{"type":44,"value":230},{"type":38,"tag":214,"props":295,"children":296},{"style":227},[297],{"type":44,"value":298}," test-webview-smoke",{"type":38,"tag":214,"props":300,"children":301},{"style":227},[302],{"type":44,"value":303}," -Dplatform=macos\n",{"type":38,"tag":47,"props":305,"children":306},{},[307,309,315],{"type":44,"value":308},"The runner must pass an automation server into ",{"type":38,"tag":53,"props":310,"children":312},{"className":311},[],[313],{"type":44,"value":314},"RuntimeOptions",{"type":44,"value":201},{"type":38,"tag":203,"props":317,"children":320},{"className":318,"code":319,"language":224,"meta":208,"style":208},"language-zig shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","const server = native_sdk.automation.Server.init(io, \".zig-cache\u002Fnative-sdk-automation\", \"My App\");\nvar runtime = native_sdk.Runtime.init(.{\n    .platform = my_platform,\n    .automation = server,\n});\n",[321],{"type":38,"tag":53,"props":322,"children":323},{"__ignoreMap":208},[324,332,340,349,358],{"type":38,"tag":214,"props":325,"children":326},{"class":216,"line":217},[327],{"type":38,"tag":214,"props":328,"children":329},{},[330],{"type":44,"value":331},"const server = native_sdk.automation.Server.init(io, \".zig-cache\u002Fnative-sdk-automation\", \"My App\");\n",{"type":38,"tag":214,"props":333,"children":334},{"class":216,"line":284},[335],{"type":38,"tag":214,"props":336,"children":337},{},[338],{"type":44,"value":339},"var runtime = native_sdk.Runtime.init(.{\n",{"type":38,"tag":214,"props":341,"children":343},{"class":216,"line":342},3,[344],{"type":38,"tag":214,"props":345,"children":346},{},[347],{"type":44,"value":348},"    .platform = my_platform,\n",{"type":38,"tag":214,"props":350,"children":352},{"class":216,"line":351},4,[353],{"type":38,"tag":214,"props":354,"children":355},{},[356],{"type":44,"value":357},"    .automation = server,\n",{"type":38,"tag":214,"props":359,"children":361},{"class":216,"line":360},5,[362],{"type":38,"tag":214,"props":363,"children":364},{},[365],{"type":44,"value":366},"});\n",{"type":38,"tag":47,"props":368,"children":369},{},[370,372,377],{"type":44,"value":371},"Apps built without ",{"type":38,"tag":53,"props":373,"children":375},{"className":374},[],[376],{"type":44,"value":199},{"type":44,"value":378}," usually ignore automation files.",{"type":38,"tag":67,"props":380,"children":382},{"id":381},"commands",[383],{"type":44,"value":384},"Commands",{"type":38,"tag":203,"props":386,"children":388},{"className":205,"code":387,"language":207,"meta":208,"style":208},"native automate wait\nnative automate assert 'gpu_nonblank=true' 'role=button name=\"Reset\"'\nnative automate assert --absent 'error event='\nnative automate list\nnative automate snapshot\nnative automate reload\nnative automate screenshot inbox-canvas\nnative automate screenshot inbox-canvas 2\nnative automate widget-action canvas 2 press\nnative automate widget-click canvas 3\nnative automate widget-hold canvas 3\nnative automate widget-context-press canvas 3\nnative automate widget-drag canvas 4 0.25 0.82\nnative automate widget-wheel canvas 5 18\nnative automate widget-key canvas tab\nnative automate widget-key canvas cmd+c\nnative automate widget-pinch canvas 1.5\nnative automate widget-pinch canvas 0.5 120 80\nnative automate profile on\nnative automate profile off\nnative automate bridge '{\"id\":\"smoke\",\"command\":\"native.ping\",\"payload\":{\"source\":\"automation\"}}'\n",[389],{"type":38,"tag":53,"props":390,"children":391},{"__ignoreMap":208},[392,410,456,489,505,521,538,560,587,619,645,670,695,731,762,788,813,839,874,896,917],{"type":38,"tag":214,"props":393,"children":394},{"class":216,"line":217},[395,400,405],{"type":38,"tag":214,"props":396,"children":397},{"style":221},[398],{"type":44,"value":399},"native",{"type":38,"tag":214,"props":401,"children":402},{"style":227},[403],{"type":44,"value":404}," automate",{"type":38,"tag":214,"props":406,"children":407},{"style":227},[408],{"type":44,"value":409}," wait\n",{"type":38,"tag":214,"props":411,"children":412},{"class":216,"line":284},[413,417,421,426,432,437,442,446,451],{"type":38,"tag":214,"props":414,"children":415},{"style":221},[416],{"type":44,"value":399},{"type":38,"tag":214,"props":418,"children":419},{"style":227},[420],{"type":44,"value":404},{"type":38,"tag":214,"props":422,"children":423},{"style":227},[424],{"type":44,"value":425}," assert",{"type":38,"tag":214,"props":427,"children":429},{"style":428},"--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF",[430],{"type":44,"value":431}," '",{"type":38,"tag":214,"props":433,"children":434},{"style":227},[435],{"type":44,"value":436},"gpu_nonblank=true",{"type":38,"tag":214,"props":438,"children":439},{"style":428},[440],{"type":44,"value":441},"'",{"type":38,"tag":214,"props":443,"children":444},{"style":428},[445],{"type":44,"value":431},{"type":38,"tag":214,"props":447,"children":448},{"style":227},[449],{"type":44,"value":450},"role=button name=\"Reset\"",{"type":38,"tag":214,"props":452,"children":453},{"style":428},[454],{"type":44,"value":455},"'\n",{"type":38,"tag":214,"props":457,"children":458},{"class":216,"line":342},[459,463,467,471,476,480,485],{"type":38,"tag":214,"props":460,"children":461},{"style":221},[462],{"type":44,"value":399},{"type":38,"tag":214,"props":464,"children":465},{"style":227},[466],{"type":44,"value":404},{"type":38,"tag":214,"props":468,"children":469},{"style":227},[470],{"type":44,"value":425},{"type":38,"tag":214,"props":472,"children":473},{"style":227},[474],{"type":44,"value":475}," --absent",{"type":38,"tag":214,"props":477,"children":478},{"style":428},[479],{"type":44,"value":431},{"type":38,"tag":214,"props":481,"children":482},{"style":227},[483],{"type":44,"value":484},"error event=",{"type":38,"tag":214,"props":486,"children":487},{"style":428},[488],{"type":44,"value":455},{"type":38,"tag":214,"props":490,"children":491},{"class":216,"line":351},[492,496,500],{"type":38,"tag":214,"props":493,"children":494},{"style":221},[495],{"type":44,"value":399},{"type":38,"tag":214,"props":497,"children":498},{"style":227},[499],{"type":44,"value":404},{"type":38,"tag":214,"props":501,"children":502},{"style":227},[503],{"type":44,"value":504}," list\n",{"type":38,"tag":214,"props":506,"children":507},{"class":216,"line":360},[508,512,516],{"type":38,"tag":214,"props":509,"children":510},{"style":221},[511],{"type":44,"value":399},{"type":38,"tag":214,"props":513,"children":514},{"style":227},[515],{"type":44,"value":404},{"type":38,"tag":214,"props":517,"children":518},{"style":227},[519],{"type":44,"value":520}," snapshot\n",{"type":38,"tag":214,"props":522,"children":524},{"class":216,"line":523},6,[525,529,533],{"type":38,"tag":214,"props":526,"children":527},{"style":221},[528],{"type":44,"value":399},{"type":38,"tag":214,"props":530,"children":531},{"style":227},[532],{"type":44,"value":404},{"type":38,"tag":214,"props":534,"children":535},{"style":227},[536],{"type":44,"value":537}," reload\n",{"type":38,"tag":214,"props":539,"children":541},{"class":216,"line":540},7,[542,546,550,555],{"type":38,"tag":214,"props":543,"children":544},{"style":221},[545],{"type":44,"value":399},{"type":38,"tag":214,"props":547,"children":548},{"style":227},[549],{"type":44,"value":404},{"type":38,"tag":214,"props":551,"children":552},{"style":227},[553],{"type":44,"value":554}," screenshot",{"type":38,"tag":214,"props":556,"children":557},{"style":227},[558],{"type":44,"value":559}," inbox-canvas\n",{"type":38,"tag":214,"props":561,"children":563},{"class":216,"line":562},8,[564,568,572,576,581],{"type":38,"tag":214,"props":565,"children":566},{"style":221},[567],{"type":44,"value":399},{"type":38,"tag":214,"props":569,"children":570},{"style":227},[571],{"type":44,"value":404},{"type":38,"tag":214,"props":573,"children":574},{"style":227},[575],{"type":44,"value":554},{"type":38,"tag":214,"props":577,"children":578},{"style":227},[579],{"type":44,"value":580}," inbox-canvas",{"type":38,"tag":214,"props":582,"children":584},{"style":583},"--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C",[585],{"type":44,"value":586}," 2\n",{"type":38,"tag":214,"props":588,"children":590},{"class":216,"line":589},9,[591,595,599,604,609,614],{"type":38,"tag":214,"props":592,"children":593},{"style":221},[594],{"type":44,"value":399},{"type":38,"tag":214,"props":596,"children":597},{"style":227},[598],{"type":44,"value":404},{"type":38,"tag":214,"props":600,"children":601},{"style":227},[602],{"type":44,"value":603}," widget-action",{"type":38,"tag":214,"props":605,"children":606},{"style":227},[607],{"type":44,"value":608}," canvas",{"type":38,"tag":214,"props":610,"children":611},{"style":583},[612],{"type":44,"value":613}," 2",{"type":38,"tag":214,"props":615,"children":616},{"style":227},[617],{"type":44,"value":618}," press\n",{"type":38,"tag":214,"props":620,"children":622},{"class":216,"line":621},10,[623,627,631,636,640],{"type":38,"tag":214,"props":624,"children":625},{"style":221},[626],{"type":44,"value":399},{"type":38,"tag":214,"props":628,"children":629},{"style":227},[630],{"type":44,"value":404},{"type":38,"tag":214,"props":632,"children":633},{"style":227},[634],{"type":44,"value":635}," widget-click",{"type":38,"tag":214,"props":637,"children":638},{"style":227},[639],{"type":44,"value":608},{"type":38,"tag":214,"props":641,"children":642},{"style":583},[643],{"type":44,"value":644}," 3\n",{"type":38,"tag":214,"props":646,"children":648},{"class":216,"line":647},11,[649,653,657,662,666],{"type":38,"tag":214,"props":650,"children":651},{"style":221},[652],{"type":44,"value":399},{"type":38,"tag":214,"props":654,"children":655},{"style":227},[656],{"type":44,"value":404},{"type":38,"tag":214,"props":658,"children":659},{"style":227},[660],{"type":44,"value":661}," widget-hold",{"type":38,"tag":214,"props":663,"children":664},{"style":227},[665],{"type":44,"value":608},{"type":38,"tag":214,"props":667,"children":668},{"style":583},[669],{"type":44,"value":644},{"type":38,"tag":214,"props":671,"children":673},{"class":216,"line":672},12,[674,678,682,687,691],{"type":38,"tag":214,"props":675,"children":676},{"style":221},[677],{"type":44,"value":399},{"type":38,"tag":214,"props":679,"children":680},{"style":227},[681],{"type":44,"value":404},{"type":38,"tag":214,"props":683,"children":684},{"style":227},[685],{"type":44,"value":686}," widget-context-press",{"type":38,"tag":214,"props":688,"children":689},{"style":227},[690],{"type":44,"value":608},{"type":38,"tag":214,"props":692,"children":693},{"style":583},[694],{"type":44,"value":644},{"type":38,"tag":214,"props":696,"children":698},{"class":216,"line":697},13,[699,703,707,712,716,721,726],{"type":38,"tag":214,"props":700,"children":701},{"style":221},[702],{"type":44,"value":399},{"type":38,"tag":214,"props":704,"children":705},{"style":227},[706],{"type":44,"value":404},{"type":38,"tag":214,"props":708,"children":709},{"style":227},[710],{"type":44,"value":711}," widget-drag",{"type":38,"tag":214,"props":713,"children":714},{"style":227},[715],{"type":44,"value":608},{"type":38,"tag":214,"props":717,"children":718},{"style":583},[719],{"type":44,"value":720}," 4",{"type":38,"tag":214,"props":722,"children":723},{"style":583},[724],{"type":44,"value":725}," 0.25",{"type":38,"tag":214,"props":727,"children":728},{"style":583},[729],{"type":44,"value":730}," 0.82\n",{"type":38,"tag":214,"props":732,"children":734},{"class":216,"line":733},14,[735,739,743,748,752,757],{"type":38,"tag":214,"props":736,"children":737},{"style":221},[738],{"type":44,"value":399},{"type":38,"tag":214,"props":740,"children":741},{"style":227},[742],{"type":44,"value":404},{"type":38,"tag":214,"props":744,"children":745},{"style":227},[746],{"type":44,"value":747}," widget-wheel",{"type":38,"tag":214,"props":749,"children":750},{"style":227},[751],{"type":44,"value":608},{"type":38,"tag":214,"props":753,"children":754},{"style":583},[755],{"type":44,"value":756}," 5",{"type":38,"tag":214,"props":758,"children":759},{"style":583},[760],{"type":44,"value":761}," 18\n",{"type":38,"tag":214,"props":763,"children":765},{"class":216,"line":764},15,[766,770,774,779,783],{"type":38,"tag":214,"props":767,"children":768},{"style":221},[769],{"type":44,"value":399},{"type":38,"tag":214,"props":771,"children":772},{"style":227},[773],{"type":44,"value":404},{"type":38,"tag":214,"props":775,"children":776},{"style":227},[777],{"type":44,"value":778}," widget-key",{"type":38,"tag":214,"props":780,"children":781},{"style":227},[782],{"type":44,"value":608},{"type":38,"tag":214,"props":784,"children":785},{"style":227},[786],{"type":44,"value":787}," tab\n",{"type":38,"tag":214,"props":789,"children":791},{"class":216,"line":790},16,[792,796,800,804,808],{"type":38,"tag":214,"props":793,"children":794},{"style":221},[795],{"type":44,"value":399},{"type":38,"tag":214,"props":797,"children":798},{"style":227},[799],{"type":44,"value":404},{"type":38,"tag":214,"props":801,"children":802},{"style":227},[803],{"type":44,"value":778},{"type":38,"tag":214,"props":805,"children":806},{"style":227},[807],{"type":44,"value":608},{"type":38,"tag":214,"props":809,"children":810},{"style":227},[811],{"type":44,"value":812}," cmd+c\n",{"type":38,"tag":214,"props":814,"children":816},{"class":216,"line":815},17,[817,821,825,830,834],{"type":38,"tag":214,"props":818,"children":819},{"style":221},[820],{"type":44,"value":399},{"type":38,"tag":214,"props":822,"children":823},{"style":227},[824],{"type":44,"value":404},{"type":38,"tag":214,"props":826,"children":827},{"style":227},[828],{"type":44,"value":829}," widget-pinch",{"type":38,"tag":214,"props":831,"children":832},{"style":227},[833],{"type":44,"value":608},{"type":38,"tag":214,"props":835,"children":836},{"style":583},[837],{"type":44,"value":838}," 1.5\n",{"type":38,"tag":214,"props":840,"children":842},{"class":216,"line":841},18,[843,847,851,855,859,864,869],{"type":38,"tag":214,"props":844,"children":845},{"style":221},[846],{"type":44,"value":399},{"type":38,"tag":214,"props":848,"children":849},{"style":227},[850],{"type":44,"value":404},{"type":38,"tag":214,"props":852,"children":853},{"style":227},[854],{"type":44,"value":829},{"type":38,"tag":214,"props":856,"children":857},{"style":227},[858],{"type":44,"value":608},{"type":38,"tag":214,"props":860,"children":861},{"style":583},[862],{"type":44,"value":863}," 0.5",{"type":38,"tag":214,"props":865,"children":866},{"style":583},[867],{"type":44,"value":868}," 120",{"type":38,"tag":214,"props":870,"children":871},{"style":583},[872],{"type":44,"value":873}," 80\n",{"type":38,"tag":214,"props":875,"children":877},{"class":216,"line":876},19,[878,882,886,891],{"type":38,"tag":214,"props":879,"children":880},{"style":221},[881],{"type":44,"value":399},{"type":38,"tag":214,"props":883,"children":884},{"style":227},[885],{"type":44,"value":404},{"type":38,"tag":214,"props":887,"children":888},{"style":227},[889],{"type":44,"value":890}," profile",{"type":38,"tag":214,"props":892,"children":893},{"style":227},[894],{"type":44,"value":895}," on\n",{"type":38,"tag":214,"props":897,"children":899},{"class":216,"line":898},20,[900,904,908,912],{"type":38,"tag":214,"props":901,"children":902},{"style":221},[903],{"type":44,"value":399},{"type":38,"tag":214,"props":905,"children":906},{"style":227},[907],{"type":44,"value":404},{"type":38,"tag":214,"props":909,"children":910},{"style":227},[911],{"type":44,"value":890},{"type":38,"tag":214,"props":913,"children":914},{"style":227},[915],{"type":44,"value":916}," off\n",{"type":38,"tag":214,"props":918,"children":920},{"class":216,"line":919},21,[921,925,929,934,938,943],{"type":38,"tag":214,"props":922,"children":923},{"style":221},[924],{"type":44,"value":399},{"type":38,"tag":214,"props":926,"children":927},{"style":227},[928],{"type":44,"value":404},{"type":38,"tag":214,"props":930,"children":931},{"style":227},[932],{"type":44,"value":933}," bridge",{"type":38,"tag":214,"props":935,"children":936},{"style":428},[937],{"type":44,"value":431},{"type":38,"tag":214,"props":939,"children":940},{"style":227},[941],{"type":44,"value":942},"{\"id\":\"smoke\",\"command\":\"native.ping\",\"payload\":{\"source\":\"automation\"}}",{"type":38,"tag":214,"props":944,"children":945},{"style":428},[946],{"type":44,"value":455},{"type":38,"tag":47,"props":948,"children":949},{},[950],{"type":44,"value":951},"If using the repository-built CLI:",{"type":38,"tag":203,"props":953,"children":955},{"className":205,"code":954,"language":207,"meta":208,"style":208},"zig-out\u002Fbin\u002Fnative automate wait\nzig-out\u002Fbin\u002Fnative automate snapshot\n",[956],{"type":38,"tag":53,"props":957,"children":958},{"__ignoreMap":208},[959,975],{"type":38,"tag":214,"props":960,"children":961},{"class":216,"line":217},[962,967,971],{"type":38,"tag":214,"props":963,"children":964},{"style":221},[965],{"type":44,"value":966},"zig-out\u002Fbin\u002Fnative",{"type":38,"tag":214,"props":968,"children":969},{"style":227},[970],{"type":44,"value":404},{"type":38,"tag":214,"props":972,"children":973},{"style":227},[974],{"type":44,"value":409},{"type":38,"tag":214,"props":976,"children":977},{"class":216,"line":284},[978,982,986],{"type":38,"tag":214,"props":979,"children":980},{"style":221},[981],{"type":44,"value":966},{"type":38,"tag":214,"props":983,"children":984},{"style":227},[985],{"type":44,"value":404},{"type":38,"tag":214,"props":987,"children":988},{"style":227},[989],{"type":44,"value":520},{"type":38,"tag":67,"props":991,"children":993},{"id":992},"snapshot-assertions-automate-assert",[994,996,1002],{"type":44,"value":995},"Snapshot assertions (",{"type":38,"tag":53,"props":997,"children":999},{"className":998},[],[1000],{"type":44,"value":1001},"automate assert",{"type":44,"value":1003},")",{"type":38,"tag":47,"props":1005,"children":1006},{},[1007,1009,1015,1017,1023],{"type":44,"value":1008},"Prefer ",{"type":38,"tag":53,"props":1010,"children":1012},{"className":1011},[],[1013],{"type":44,"value":1014},"native automate assert",{"type":44,"value":1016}," over ",{"type":38,"tag":53,"props":1018,"children":1020},{"className":1019},[],[1021],{"type":44,"value":1022},"snapshot | grep",{"type":44,"value":1024}," chains: it polls, so no sleeps, and its failure output carries the evidence (each missing pattern plus the snapshot tail).",{"type":38,"tag":203,"props":1026,"children":1028},{"className":205,"code":1027,"language":207,"meta":208,"style":208},"native automate assert 'gpu_nonblank=true' 'role=button name=\"Reset\"' 'count: 0'\nnative automate assert --timeout-ms 10000 '4 open'\nnative automate assert --absent 'error event=' 'dispatch_errors=[1-9]'\n",[1029],{"type":38,"tag":53,"props":1030,"children":1031},{"__ignoreMap":208},[1032,1084,1122],{"type":38,"tag":214,"props":1033,"children":1034},{"class":216,"line":217},[1035,1039,1043,1047,1051,1055,1059,1063,1067,1071,1075,1080],{"type":38,"tag":214,"props":1036,"children":1037},{"style":221},[1038],{"type":44,"value":399},{"type":38,"tag":214,"props":1040,"children":1041},{"style":227},[1042],{"type":44,"value":404},{"type":38,"tag":214,"props":1044,"children":1045},{"style":227},[1046],{"type":44,"value":425},{"type":38,"tag":214,"props":1048,"children":1049},{"style":428},[1050],{"type":44,"value":431},{"type":38,"tag":214,"props":1052,"children":1053},{"style":227},[1054],{"type":44,"value":436},{"type":38,"tag":214,"props":1056,"children":1057},{"style":428},[1058],{"type":44,"value":441},{"type":38,"tag":214,"props":1060,"children":1061},{"style":428},[1062],{"type":44,"value":431},{"type":38,"tag":214,"props":1064,"children":1065},{"style":227},[1066],{"type":44,"value":450},{"type":38,"tag":214,"props":1068,"children":1069},{"style":428},[1070],{"type":44,"value":441},{"type":38,"tag":214,"props":1072,"children":1073},{"style":428},[1074],{"type":44,"value":431},{"type":38,"tag":214,"props":1076,"children":1077},{"style":227},[1078],{"type":44,"value":1079},"count: 0",{"type":38,"tag":214,"props":1081,"children":1082},{"style":428},[1083],{"type":44,"value":455},{"type":38,"tag":214,"props":1085,"children":1086},{"class":216,"line":284},[1087,1091,1095,1099,1104,1109,1113,1118],{"type":38,"tag":214,"props":1088,"children":1089},{"style":221},[1090],{"type":44,"value":399},{"type":38,"tag":214,"props":1092,"children":1093},{"style":227},[1094],{"type":44,"value":404},{"type":38,"tag":214,"props":1096,"children":1097},{"style":227},[1098],{"type":44,"value":425},{"type":38,"tag":214,"props":1100,"children":1101},{"style":227},[1102],{"type":44,"value":1103}," --timeout-ms",{"type":38,"tag":214,"props":1105,"children":1106},{"style":583},[1107],{"type":44,"value":1108}," 10000",{"type":38,"tag":214,"props":1110,"children":1111},{"style":428},[1112],{"type":44,"value":431},{"type":38,"tag":214,"props":1114,"children":1115},{"style":227},[1116],{"type":44,"value":1117},"4 open",{"type":38,"tag":214,"props":1119,"children":1120},{"style":428},[1121],{"type":44,"value":455},{"type":38,"tag":214,"props":1123,"children":1124},{"class":216,"line":342},[1125,1129,1133,1137,1141,1145,1149,1153,1157,1162],{"type":38,"tag":214,"props":1126,"children":1127},{"style":221},[1128],{"type":44,"value":399},{"type":38,"tag":214,"props":1130,"children":1131},{"style":227},[1132],{"type":44,"value":404},{"type":38,"tag":214,"props":1134,"children":1135},{"style":227},[1136],{"type":44,"value":425},{"type":38,"tag":214,"props":1138,"children":1139},{"style":227},[1140],{"type":44,"value":475},{"type":38,"tag":214,"props":1142,"children":1143},{"style":428},[1144],{"type":44,"value":431},{"type":38,"tag":214,"props":1146,"children":1147},{"style":227},[1148],{"type":44,"value":484},{"type":38,"tag":214,"props":1150,"children":1151},{"style":428},[1152],{"type":44,"value":441},{"type":38,"tag":214,"props":1154,"children":1155},{"style":428},[1156],{"type":44,"value":431},{"type":38,"tag":214,"props":1158,"children":1159},{"style":227},[1160],{"type":44,"value":1161},"dispatch_errors=[1-9]",{"type":38,"tag":214,"props":1163,"children":1164},{"style":428},[1165],{"type":44,"value":455},{"type":38,"tag":47,"props":1167,"children":1168},{},[1169],{"type":44,"value":1170},"Semantics:",{"type":38,"tag":74,"props":1172,"children":1173},{},[1174,1187,1206,1217,1291],{"type":38,"tag":78,"props":1175,"children":1176},{},[1177,1179,1185],{"type":44,"value":1178},"Every argument is a regex that must match somewhere in ",{"type":38,"tag":53,"props":1180,"children":1182},{"className":1181},[],[1183],{"type":44,"value":1184},"snapshot.txt",{"type":44,"value":1186},". The command polls (100ms interval) until all match, then exits 0.",{"type":38,"tag":78,"props":1188,"children":1189},{},[1190,1196,1198,1204],{"type":38,"tag":53,"props":1191,"children":1193},{"className":1192},[],[1194],{"type":44,"value":1195},"--timeout-ms \u003Cn>",{"type":44,"value":1197}," bounds the polling (default 30000). On timeout it prints ",{"type":38,"tag":53,"props":1199,"children":1201},{"className":1200},[],[1202],{"type":44,"value":1203},"missing: \u003Cpattern>",{"type":44,"value":1205}," for each unmatched pattern, the last 20 snapshot lines, and exits non-zero — CI-friendly, no wrapper script needed.",{"type":38,"tag":78,"props":1207,"children":1208},{},[1209,1215],{"type":38,"tag":53,"props":1210,"children":1212},{"className":1211},[],[1213],{"type":44,"value":1214},"--absent",{"type":44,"value":1216}," inverts the whole invocation: every pattern must NOT match (poll until gone). Mix presence and absence by running two invocations.",{"type":38,"tag":78,"props":1218,"children":1219},{},[1220,1222,1227,1229,1235,1237,1243,1244,1250,1252,1258,1260,1266,1268,1274,1275,1281,1283,1289],{"type":44,"value":1221},"Supported regex subset: literals, ",{"type":38,"tag":53,"props":1223,"children":1225},{"className":1224},[],[1226],{"type":44,"value":90},{"type":44,"value":1228},", postfix ",{"type":38,"tag":53,"props":1230,"children":1232},{"className":1231},[],[1233],{"type":44,"value":1234},"*",{"type":44,"value":1236}," ",{"type":38,"tag":53,"props":1238,"children":1240},{"className":1239},[],[1241],{"type":44,"value":1242},"+",{"type":44,"value":1236},{"type":38,"tag":53,"props":1245,"children":1247},{"className":1246},[],[1248],{"type":44,"value":1249},"?",{"type":44,"value":1251},", line anchors ",{"type":38,"tag":53,"props":1253,"children":1255},{"className":1254},[],[1256],{"type":44,"value":1257},"^",{"type":44,"value":1259},"\u002F",{"type":38,"tag":53,"props":1261,"children":1263},{"className":1262},[],[1264],{"type":44,"value":1265},"$",{"type":44,"value":1267},", classes ",{"type":38,"tag":53,"props":1269,"children":1271},{"className":1270},[],[1272],{"type":44,"value":1273},"[a-z]",{"type":44,"value":1259},{"type":38,"tag":53,"props":1276,"children":1278},{"className":1277},[],[1279],{"type":44,"value":1280},"[^0-9]",{"type":44,"value":1282},", and ",{"type":38,"tag":53,"props":1284,"children":1286},{"className":1285},[],[1287],{"type":44,"value":1288},"\\d \\w \\s",{"type":44,"value":1290}," (with uppercase negations). No groups or alternation — pass multiple patterns instead.",{"type":38,"tag":78,"props":1292,"children":1293},{},[1294,1296,1302,1304,1309,1310,1316],{"type":44,"value":1295},"Quote patterns in single quotes so the shell leaves ",{"type":38,"tag":53,"props":1297,"children":1299},{"className":1298},[],[1300],{"type":44,"value":1301},"\"",{"type":44,"value":1303},", ",{"type":38,"tag":53,"props":1305,"children":1307},{"className":1306},[],[1308],{"type":44,"value":1265},{"type":44,"value":1282},{"type":38,"tag":53,"props":1311,"children":1313},{"className":1312},[],[1314],{"type":44,"value":1315},"\\d",{"type":44,"value":1317}," alone.",{"type":38,"tag":67,"props":1319,"children":1321},{"id":1320},"standard-workflow",[1322],{"type":44,"value":1323},"Standard workflow",{"type":38,"tag":1325,"props":1326,"children":1327},"ol",{},[1328,1333,1359,1379,1391,1403,1432,1482,1494,1513,1585,1675,1702,1768,1780],{"type":38,"tag":78,"props":1329,"children":1330},{},[1331],{"type":44,"value":1332},"Start the app with automation enabled.",{"type":38,"tag":78,"props":1334,"children":1335},{},[1336,1338,1344,1346,1351,1353,1358],{"type":44,"value":1337},"Run ",{"type":38,"tag":53,"props":1339,"children":1341},{"className":1340},[],[1342],{"type":44,"value":1343},"native automate wait",{"type":44,"value":1345}," to block until ",{"type":38,"tag":53,"props":1347,"children":1349},{"className":1348},[],[1350],{"type":44,"value":1184},{"type":44,"value":1352}," contains ",{"type":38,"tag":53,"props":1354,"children":1356},{"className":1355},[],[1357],{"type":44,"value":88},{"type":44,"value":90},{"type":38,"tag":78,"props":1360,"children":1361},{},[1362,1363,1369,1371,1377],{"type":44,"value":1337},{"type":38,"tag":53,"props":1364,"children":1366},{"className":1365},[],[1367],{"type":44,"value":1368},"native automate assert '\u003Cpattern>' ...",{"type":44,"value":1370}," for state checks, or ",{"type":38,"tag":53,"props":1372,"children":1374},{"className":1373},[],[1375],{"type":44,"value":1376},"native automate snapshot",{"type":44,"value":1378}," to eyeball app\u002Fwindow\u002Fsource metadata.",{"type":38,"tag":78,"props":1380,"children":1381},{},[1382,1383,1389],{"type":44,"value":1337},{"type":38,"tag":53,"props":1384,"children":1386},{"className":1385},[],[1387],{"type":44,"value":1388},"native automate list",{"type":44,"value":1390}," to inspect window summaries.",{"type":38,"tag":78,"props":1392,"children":1393},{},[1394,1395,1401],{"type":44,"value":1337},{"type":38,"tag":53,"props":1396,"children":1398},{"className":1397},[],[1399],{"type":44,"value":1400},"native automate bridge '...'",{"type":44,"value":1402}," for bridge round-trip checks.",{"type":38,"tag":78,"props":1404,"children":1405},{},[1406,1408,1414,1416,1422,1424,1430],{"type":44,"value":1407},"Use ",{"type":38,"tag":53,"props":1409,"children":1411},{"className":1410},[],[1412],{"type":44,"value":1413},"native automate widget-action \u003Cview-label> \u003Cwidget-id> \u003Caction> [value]",{"type":44,"value":1415}," to exercise retained canvas widget actions. ",{"type":38,"tag":53,"props":1417,"children":1419},{"className":1418},[],[1420],{"type":44,"value":1421},"set_text",{"type":44,"value":1423}," routes through the SAME input path real typing uses (focus, select-all, then a text-input event), so a TEA app's ",{"type":38,"tag":53,"props":1425,"children":1427},{"className":1426},[],[1428],{"type":44,"value":1429},"on_input",{"type":44,"value":1431}," mirror receives the edits and model state stays consistent with the on-screen field — it is not a presentation-only write.",{"type":38,"tag":78,"props":1433,"children":1434},{},[1435,1436,1442,1444,1450,1452,1458,1460,1465,1467,1473,1475,1480],{"type":44,"value":1407},{"type":38,"tag":53,"props":1437,"children":1439},{"className":1438},[],[1440],{"type":44,"value":1441},"native automate widget-click \u003Cview-label> \u003Cwidget-id>",{"type":44,"value":1443}," to exercise pointer-style retained widget routing. ",{"type":38,"tag":53,"props":1445,"children":1447},{"className":1446},[],[1448],{"type":44,"value":1449},"widget-hold \u003Cview-label> \u003Cwidget-id>",{"type":44,"value":1451}," drives a press-and-hold through the same path — pointer down, the reserved hold timer fired, then the suppressed release — so ",{"type":38,"tag":53,"props":1453,"children":1455},{"className":1454},[],[1456],{"type":44,"value":1457},"on_hold",{"type":44,"value":1459}," Msgs are live-drivable (a target without ",{"type":38,"tag":53,"props":1461,"children":1463},{"className":1462},[],[1464],{"type":44,"value":1457},{"type":44,"value":1466}," degrades to the click a real long press is). ",{"type":38,"tag":53,"props":1468,"children":1470},{"className":1469},[],[1471],{"type":44,"value":1472},"widget-context-press \u003Cview-label> \u003Cwidget-id>",{"type":44,"value":1474}," is the secondary click: it presents the widget's context menu, or dispatches ",{"type":38,"tag":53,"props":1476,"children":1478},{"className":1477},[],[1479],{"type":44,"value":1457},{"type":44,"value":1481}," immediately when the route declares none.",{"type":38,"tag":78,"props":1483,"children":1484},{},[1485,1486,1492],{"type":44,"value":1407},{"type":38,"tag":53,"props":1487,"children":1489},{"className":1488},[],[1490],{"type":44,"value":1491},"native automate widget-drag \u003Cview-label> \u003Cwidget-id> \u003Cstart-x-ratio> \u003Cend-x-ratio> [start-y-ratio end-y-ratio]",{"type":44,"value":1493}," for continuous pointer controls.",{"type":38,"tag":78,"props":1495,"children":1496},{},[1497,1498,1504,1506,1512],{"type":44,"value":1407},{"type":38,"tag":53,"props":1499,"children":1501},{"className":1500},[],[1502],{"type":44,"value":1503},"native automate widget-wheel \u003Cview-label> \u003Cwidget-id> \u003Cdelta-y>",{"type":44,"value":1505}," for retained widget scroll input. Wheel targets must be interactive\u002Fscrollable widgets — a plain layout column or text node is not a wheel target; aim at the scroll\u002Flist widget id from the snapshot. Failures land in the snapshot as named reasons: ",{"type":38,"tag":53,"props":1507,"children":1509},{"className":1508},[],[1510],{"type":44,"value":1511},"error event=automation.widget_wheel name=WheelTargetUnknown|WheelTargetNotInteractive|WheelTargetHasEmptyBounds detail=\"\u003Ccommand args>\"",{"type":44,"value":90},{"type":38,"tag":78,"props":1514,"children":1515},{},[1516,1517,1523,1525,1531,1532,1538,1539,1545,1546,1552,1553,1559,1561,1567,1569,1575,1577,1583],{"type":44,"value":1407},{"type":38,"tag":53,"props":1518,"children":1520},{"className":1519},[],[1521],{"type":44,"value":1522},"native automate widget-key \u003Cview-label> \u003Ckey> [text]",{"type":44,"value":1524}," for focused retained widget keyboard input. The key accepts modifier chords — ",{"type":38,"tag":53,"props":1526,"children":1528},{"className":1527},[],[1529],{"type":44,"value":1530},"cmd+a",{"type":44,"value":1303},{"type":38,"tag":53,"props":1533,"children":1535},{"className":1534},[],[1536],{"type":44,"value":1537},"cmd+c",{"type":44,"value":1303},{"type":38,"tag":53,"props":1540,"children":1542},{"className":1541},[],[1543],{"type":44,"value":1544},"cmd+v",{"type":44,"value":1303},{"type":38,"tag":53,"props":1547,"children":1549},{"className":1548},[],[1550],{"type":44,"value":1551},"cmd+x",{"type":44,"value":1303},{"type":38,"tag":53,"props":1554,"children":1556},{"className":1555},[],[1557],{"type":44,"value":1558},"ctrl+shift+arrowleft",{"type":44,"value":1560}," (",{"type":38,"tag":53,"props":1562,"children":1564},{"className":1563},[],[1565],{"type":44,"value":1566},"cmd",{"type":44,"value":1568}," sets the primary shortcut modifier on every platform) — so select-all\u002Fcopy\u002Fcut\u002Fpaste and shift-extended selection are drivable; after a copy, widget lines in the snapshot show the live selection as ",{"type":38,"tag":53,"props":1570,"children":1572},{"className":1571},[],[1573],{"type":44,"value":1574},"selection=a..b",{"type":44,"value":1576},", and the copied text lands on the real system clipboard (",{"type":38,"tag":53,"props":1578,"children":1580},{"className":1579},[],[1581],{"type":44,"value":1582},"pbpaste",{"type":44,"value":1584}," on macOS).",{"type":38,"tag":78,"props":1586,"children":1587},{},[1588,1589,1595,1597,1603,1604,1610,1611,1617,1619,1625,1627,1633,1635,1641,1643,1649,1651,1657,1659,1665,1667,1673],{"type":44,"value":1407},{"type":38,"tag":53,"props":1590,"children":1592},{"className":1591},[],[1593],{"type":44,"value":1594},"native automate widget-pinch \u003Cview-label> \u003Cscale> [x y]",{"type":44,"value":1596}," for trackpad pinch gestures against a gpu-surface view: the runtime dispatches the real ",{"type":38,"tag":53,"props":1598,"children":1600},{"className":1599},[],[1601],{"type":44,"value":1602},"pinch_begin",{"type":44,"value":1259},{"type":38,"tag":53,"props":1605,"children":1607},{"className":1606},[],[1608],{"type":44,"value":1609},"pinch_change",{"type":44,"value":1259},{"type":38,"tag":53,"props":1612,"children":1614},{"className":1613},[],[1615],{"type":44,"value":1616},"pinch_end",{"type":44,"value":1618}," platform events, with one change carrying ",{"type":38,"tag":53,"props":1620,"children":1622},{"className":1621},[],[1623],{"type":44,"value":1624},"scale - 1",{"type":44,"value":1626},". ",{"type":38,"tag":53,"props":1628,"children":1630},{"className":1629},[],[1631],{"type":44,"value":1632},"\u003Cscale>",{"type":44,"value":1634}," is the FINAL multiplicative zoom for the gesture — the cumulative gesture scale (the product of ",{"type":38,"tag":53,"props":1636,"children":1638},{"className":1637},[],[1639],{"type":44,"value":1640},"1 + delta",{"type":44,"value":1642},") lands exactly on it — ",{"type":38,"tag":53,"props":1644,"children":1646},{"className":1645},[],[1647],{"type":44,"value":1648},"1.5",{"type":44,"value":1650}," zooms in 50%, ",{"type":38,"tag":53,"props":1652,"children":1654},{"className":1653},[],[1655],{"type":44,"value":1656},"0.5",{"type":44,"value":1658}," zooms out to half. The optional anchor point is view-local points, defaulting to the view center. Apps hear it through the pinch channel (",{"type":38,"tag":53,"props":1660,"children":1662},{"className":1661},[],[1663],{"type":44,"value":1664},"Options.on_pinch",{"type":44,"value":1666}," \u002F the TS core's ",{"type":38,"tag":53,"props":1668,"children":1670},{"className":1669},[],[1671],{"type":44,"value":1672},"pinchMsg",{"type":44,"value":1674},").",{"type":38,"tag":78,"props":1676,"children":1677},{},[1678,1679,1685,1687,1692,1694,1700],{"type":44,"value":1407},{"type":38,"tag":53,"props":1680,"children":1682},{"className":1681},[],[1683],{"type":44,"value":1684},"native automate screenshot \u003Cview-label> [scale]",{"type":44,"value":1686}," to capture the named ",{"type":38,"tag":53,"props":1688,"children":1690},{"className":1689},[],[1691],{"type":44,"value":133},{"type":44,"value":1693}," view's canvas as ",{"type":38,"tag":53,"props":1695,"children":1697},{"className":1696},[],[1698],{"type":44,"value":1699},"screenshot-\u003Cview-label>.png",{"type":44,"value":1701}," (the CLI prints the artifact path and waits for the file).",{"type":38,"tag":78,"props":1703,"children":1704},{},[1705,1706,1712,1714,1720,1722,1727,1729,1735,1737,1743,1745,1751,1753,1759,1761,1767],{"type":44,"value":1407},{"type":38,"tag":53,"props":1707,"children":1709},{"className":1708},[],[1710],{"type":44,"value":1711},"native automate tray-action \u003Citem-id>",{"type":44,"value":1713}," to select a status-item dropdown row through the same platform event a real menu-bar click emits (command dispatch with source ",{"type":38,"tag":53,"props":1715,"children":1717},{"className":1716},[],[1718],{"type":44,"value":1719},".tray",{"type":44,"value":1721},"). The live tray is visible in ",{"type":38,"tag":53,"props":1723,"children":1725},{"className":1724},[],[1726],{"type":44,"value":1184},{"type":44,"value":1728}," as a ",{"type":38,"tag":53,"props":1730,"children":1732},{"className":1731},[],[1733],{"type":44,"value":1734},"tray title=\"...\" items=N",{"type":44,"value":1736}," line followed by ",{"type":38,"tag":53,"props":1738,"children":1740},{"className":1739},[],[1741],{"type":44,"value":1742},"  tray-item #id label=\"...\" command=\"...\" enabled=...",{"type":44,"value":1744}," rows — the macOS menu bar is outside every window capture, so the snapshot is the only automation evidence the model-driven tray exists, and the ",{"type":38,"tag":53,"props":1746,"children":1748},{"className":1747},[],[1749],{"type":44,"value":1750},"#id",{"type":44,"value":1752}," there is what ",{"type":38,"tag":53,"props":1754,"children":1756},{"className":1755},[],[1757],{"type":44,"value":1758},"tray-action",{"type":44,"value":1760}," takes. Unknown ids degrade into the dispatch-error ring as ",{"type":38,"tag":53,"props":1762,"children":1764},{"className":1763},[],[1765],{"type":44,"value":1766},"automation.tray_action",{"type":44,"value":90},{"type":38,"tag":78,"props":1769,"children":1770},{},[1771,1772,1778],{"type":44,"value":1407},{"type":38,"tag":53,"props":1773,"children":1775},{"className":1774},[],[1776],{"type":44,"value":1777},"native automate reload",{"type":44,"value":1779}," to request a WebView reload.",{"type":38,"tag":78,"props":1781,"children":1782},{},[1783,1784,1790,1792,1797,1799,1805,1807,1813,1814,1820,1821,1827,1828,1834,1835,1841,1842,1848,1849,1855,1856,1862,1863,1869,1870,1876,1877,1883,1885,1891,1893,1899,1901,1907],{"type":44,"value":1407},{"type":38,"tag":53,"props":1785,"children":1787},{"className":1786},[],[1788],{"type":44,"value":1789},"native automate profile on",{"type":44,"value":1791}," to enable per-stage frame timing: while on, ",{"type":38,"tag":53,"props":1793,"children":1795},{"className":1794},[],[1796],{"type":44,"value":1184},{"type":44,"value":1798}," carries a ",{"type":38,"tag":53,"props":1800,"children":1802},{"className":1801},[],[1803],{"type":44,"value":1804},"frame_profile",{"type":44,"value":1806}," line with rolling p50\u002Fp90\u002Fmax microseconds per pipeline stage (",{"type":38,"tag":53,"props":1808,"children":1810},{"className":1809},[],[1811],{"type":44,"value":1812},"rebuild",{"type":44,"value":1303},{"type":38,"tag":53,"props":1815,"children":1817},{"className":1816},[],[1818],{"type":44,"value":1819},"layout",{"type":44,"value":1303},{"type":38,"tag":53,"props":1822,"children":1824},{"className":1823},[],[1825],{"type":44,"value":1826},"reconcile",{"type":44,"value":1303},{"type":38,"tag":53,"props":1829,"children":1831},{"className":1830},[],[1832],{"type":44,"value":1833},"emit",{"type":44,"value":1303},{"type":38,"tag":53,"props":1836,"children":1838},{"className":1837},[],[1839],{"type":44,"value":1840},"a11y",{"type":44,"value":1303},{"type":38,"tag":53,"props":1843,"children":1845},{"className":1844},[],[1846],{"type":44,"value":1847},"plan",{"type":44,"value":1303},{"type":38,"tag":53,"props":1850,"children":1852},{"className":1851},[],[1853],{"type":44,"value":1854},"patch",{"type":44,"value":1303},{"type":38,"tag":53,"props":1857,"children":1859},{"className":1858},[],[1860],{"type":44,"value":1861},"encode",{"type":44,"value":1303},{"type":38,"tag":53,"props":1864,"children":1866},{"className":1865},[],[1867],{"type":44,"value":1868},"present",{"type":44,"value":1303},{"type":38,"tag":53,"props":1871,"children":1873},{"className":1872},[],[1874],{"type":44,"value":1875},"host_decode",{"type":44,"value":1303},{"type":38,"tag":53,"props":1878,"children":1880},{"className":1879},[],[1881],{"type":44,"value":1882},"host_draw",{"type":44,"value":1884},"), each with a lifetime sample count (",{"type":38,"tag":53,"props":1886,"children":1888},{"className":1887},[],[1889],{"type":44,"value":1890},"\u003Cstage>_n=",{"type":44,"value":1892},"). Drive some interactions, then ",{"type":38,"tag":53,"props":1894,"children":1896},{"className":1895},[],[1897],{"type":44,"value":1898},"native automate snapshot | grep -o 'frame_profile.*'",{"type":44,"value":1900}," to read where frame time goes; ",{"type":38,"tag":53,"props":1902,"children":1904},{"className":1903},[],[1905],{"type":44,"value":1906},"profile off",{"type":44,"value":1908}," stops recording and drops the line. Turning it on starts a fresh sample window.",{"type":38,"tag":67,"props":1910,"children":1912},{"id":1911},"screenshots",[1913],{"type":44,"value":1914},"Screenshots",{"type":38,"tag":47,"props":1916,"children":1917},{},[1918,1924,1926,1932],{"type":38,"tag":53,"props":1919,"children":1921},{"className":1920},[],[1922],{"type":44,"value":1923},"screenshot \u003Cview-label> [scale]",{"type":44,"value":1925}," asks the runtime to rasterize the view's\ncurrent retained canvas frame through the deterministic CPU reference\nrenderer — the same pixel path the Linux software presentation uses — and\npublish it as an uncompressed PNG at\n",{"type":38,"tag":53,"props":1927,"children":1929},{"className":1928},[],[1930],{"type":44,"value":1931},".zig-cache\u002Fnative-sdk-automation\u002Fscreenshot-\u003Cview-label>.png",{"type":44,"value":1933},". The file is\nwritten atomically (temp file + rename), so its presence means the PNG is\ncomplete.",{"type":38,"tag":47,"props":1935,"children":1936},{},[1937],{"type":44,"value":1938},"Determinism semantics:",{"type":38,"tag":74,"props":1940,"children":1941},{},[1942,1955,1960,1965],{"type":38,"tag":78,"props":1943,"children":1944},{},[1945,1947,1953],{"type":44,"value":1946},"Screenshots render at scale 1 by default regardless of the display's\nbacking scale, so an unchanged scene produces byte-identical PNGs from\ncapture to capture on the same machine. Pass an explicit scale (for\nexample ",{"type":38,"tag":53,"props":1948,"children":1950},{"className":1949},[],[1951],{"type":44,"value":1952},"2",{"type":44,"value":1954},") for high-DPI pixel dimensions.",{"type":38,"tag":78,"props":1956,"children":1957},{},[1958],{"type":44,"value":1959},"Screenshots use the live retained scene, including live design tokens and\nplatform text measurement (CoreText on macOS): the layout matches what is\non screen. Glyphs are rasterized by the reference renderer from the\nbundled faces, not by the platform's font rasterizer, so screenshots are a\ndeterministic layout\u002Fstructure\u002Fcolor signal rather than a platform\nfont-rendering signal.",{"type":38,"tag":78,"props":1961,"children":1962},{},[1963],{"type":44,"value":1964},"Cross-machine byte-identity is only guaranteed where text metrics are\ndeterministic (the null platform's estimator). On platforms with a native\ntext measurement provider, text widths can differ between OS versions, so\ncompare screenshots taken on the same machine or assert on properties\n(dimensions, changed\u002Funchanged) rather than exact bytes across machines.",{"type":38,"tag":78,"props":1966,"children":1967},{},[1968,1970,1976,1978,1984],{"type":44,"value":1969},"OS-level captures (",{"type":38,"tag":53,"props":1971,"children":1973},{"className":1972},[],[1974],{"type":44,"value":1975},"screencapture -x",{"type":44,"value":1977}," on macOS) are NOT a substitute: in a\nshell without Screen Recording permission they exit 0 and silently return\nwallpaper-only images with no app window in them. If you shell out for a\nreal-pixel capture, verify the image is not blank\u002Fwallpaper before trusting\nit; ",{"type":38,"tag":53,"props":1979,"children":1981},{"className":1980},[],[1982],{"type":44,"value":1983},"automate screenshot",{"type":44,"value":1985}," plus a semantics snapshot is the reliable pair.",{"type":38,"tag":67,"props":1987,"children":1989},{"id":1988},"bridge-smoke-test-pattern",[1990],{"type":44,"value":1991},"Bridge smoke test pattern",{"type":38,"tag":47,"props":1993,"children":1994},{},[1995],{"type":44,"value":1996},"The request must be JSON with an ID, command, and payload:",{"type":38,"tag":203,"props":1998,"children":2000},{"className":205,"code":1999,"language":207,"meta":208,"style":208},"native automate bridge '{\"id\":\"smoke\",\"command\":\"native.ping\",\"payload\":{\"source\":\"automation\"}}'\n",[2001],{"type":38,"tag":53,"props":2002,"children":2003},{"__ignoreMap":208},[2004],{"type":38,"tag":214,"props":2005,"children":2006},{"class":216,"line":217},[2007,2011,2015,2019,2023,2027],{"type":38,"tag":214,"props":2008,"children":2009},{"style":221},[2010],{"type":44,"value":399},{"type":38,"tag":214,"props":2012,"children":2013},{"style":227},[2014],{"type":44,"value":404},{"type":38,"tag":214,"props":2016,"children":2017},{"style":227},[2018],{"type":44,"value":933},{"type":38,"tag":214,"props":2020,"children":2021},{"style":428},[2022],{"type":44,"value":431},{"type":38,"tag":214,"props":2024,"children":2025},{"style":227},[2026],{"type":44,"value":942},{"type":38,"tag":214,"props":2028,"children":2029},{"style":428},[2030],{"type":44,"value":455},{"type":38,"tag":47,"props":2032,"children":2033},{},[2034,2036,2042,2044,2050,2052,2058,2060,2065],{"type":44,"value":2035},"Automation sends the request with origin ",{"type":38,"tag":53,"props":2037,"children":2039},{"className":2038},[],[2040],{"type":44,"value":2041},"zero:\u002F\u002Finline",{"type":44,"value":2043},". The app's bridge policy must allow that origin or the call will reject with ",{"type":38,"tag":53,"props":2045,"children":2047},{"className":2046},[],[2048],{"type":44,"value":2049},"permission_denied",{"type":44,"value":2051},". For packaged asset origins, app code often allows ",{"type":38,"tag":53,"props":2053,"children":2055},{"className":2054},[],[2056],{"type":44,"value":2057},"zero:\u002F\u002Fapp",{"type":44,"value":2059},"; for automation smoke tests, add ",{"type":38,"tag":53,"props":2061,"children":2063},{"className":2062},[],[2064],{"type":44,"value":2041},{"type":44,"value":2066}," only when the test needs it.",{"type":38,"tag":47,"props":2068,"children":2069},{},[2070,2072,2078],{"type":44,"value":2071},"Expected response shape depends on the handler. A typical ",{"type":38,"tag":53,"props":2073,"children":2075},{"className":2074},[],[2076],{"type":44,"value":2077},"native.ping",{"type":44,"value":2079}," handler returns:",{"type":38,"tag":203,"props":2081,"children":2085},{"className":2082,"code":2083,"language":2084,"meta":208,"style":208},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{\"id\":\"smoke\",\"ok\":true,\"result\":{\"message\":\"pong\",\"count\":1}}\n","json",[2086],{"type":38,"tag":53,"props":2087,"children":2088},{"__ignoreMap":208},[2089],{"type":38,"tag":214,"props":2090,"children":2091},{"class":216,"line":217},[2092,2097,2101,2107,2111,2115,2119,2124,2128,2133,2137,2142,2146,2151,2155,2160,2164,2169,2173,2178,2182,2186,2190,2195,2199,2203,2207,2212,2216,2220,2225],{"type":38,"tag":214,"props":2093,"children":2094},{"style":428},[2095],{"type":44,"value":2096},"{",{"type":38,"tag":214,"props":2098,"children":2099},{"style":428},[2100],{"type":44,"value":1301},{"type":38,"tag":214,"props":2102,"children":2104},{"style":2103},"--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA",[2105],{"type":44,"value":2106},"id",{"type":38,"tag":214,"props":2108,"children":2109},{"style":428},[2110],{"type":44,"value":1301},{"type":38,"tag":214,"props":2112,"children":2113},{"style":428},[2114],{"type":44,"value":201},{"type":38,"tag":214,"props":2116,"children":2117},{"style":428},[2118],{"type":44,"value":1301},{"type":38,"tag":214,"props":2120,"children":2121},{"style":227},[2122],{"type":44,"value":2123},"smoke",{"type":38,"tag":214,"props":2125,"children":2126},{"style":428},[2127],{"type":44,"value":1301},{"type":38,"tag":214,"props":2129,"children":2130},{"style":428},[2131],{"type":44,"value":2132},",",{"type":38,"tag":214,"props":2134,"children":2135},{"style":428},[2136],{"type":44,"value":1301},{"type":38,"tag":214,"props":2138,"children":2139},{"style":2103},[2140],{"type":44,"value":2141},"ok",{"type":38,"tag":214,"props":2143,"children":2144},{"style":428},[2145],{"type":44,"value":1301},{"type":38,"tag":214,"props":2147,"children":2148},{"style":428},[2149],{"type":44,"value":2150},":true,",{"type":38,"tag":214,"props":2152,"children":2153},{"style":428},[2154],{"type":44,"value":1301},{"type":38,"tag":214,"props":2156,"children":2157},{"style":2103},[2158],{"type":44,"value":2159},"result",{"type":38,"tag":214,"props":2161,"children":2162},{"style":428},[2163],{"type":44,"value":1301},{"type":38,"tag":214,"props":2165,"children":2166},{"style":428},[2167],{"type":44,"value":2168},":{",{"type":38,"tag":214,"props":2170,"children":2171},{"style":428},[2172],{"type":44,"value":1301},{"type":38,"tag":214,"props":2174,"children":2175},{"style":221},[2176],{"type":44,"value":2177},"message",{"type":38,"tag":214,"props":2179,"children":2180},{"style":428},[2181],{"type":44,"value":1301},{"type":38,"tag":214,"props":2183,"children":2184},{"style":428},[2185],{"type":44,"value":201},{"type":38,"tag":214,"props":2187,"children":2188},{"style":428},[2189],{"type":44,"value":1301},{"type":38,"tag":214,"props":2191,"children":2192},{"style":227},[2193],{"type":44,"value":2194},"pong",{"type":38,"tag":214,"props":2196,"children":2197},{"style":428},[2198],{"type":44,"value":1301},{"type":38,"tag":214,"props":2200,"children":2201},{"style":428},[2202],{"type":44,"value":2132},{"type":38,"tag":214,"props":2204,"children":2205},{"style":428},[2206],{"type":44,"value":1301},{"type":38,"tag":214,"props":2208,"children":2209},{"style":221},[2210],{"type":44,"value":2211},"count",{"type":38,"tag":214,"props":2213,"children":2214},{"style":428},[2215],{"type":44,"value":1301},{"type":38,"tag":214,"props":2217,"children":2218},{"style":428},[2219],{"type":44,"value":201},{"type":38,"tag":214,"props":2221,"children":2222},{"style":583},[2223],{"type":44,"value":2224},"1",{"type":38,"tag":214,"props":2226,"children":2227},{"style":428},[2228],{"type":44,"value":2229},"}}\n",{"type":38,"tag":47,"props":2231,"children":2232},{},[2233],{"type":44,"value":2234},"If the command fails, inspect the bridge error code:",{"type":38,"tag":74,"props":2236,"children":2237},{},[2238,2249,2259,2270],{"type":38,"tag":78,"props":2239,"children":2240},{},[2241,2247],{"type":38,"tag":53,"props":2242,"children":2244},{"className":2243},[],[2245],{"type":44,"value":2246},"unknown_command",{"type":44,"value":2248},": no handler registered or wrong command name.",{"type":38,"tag":78,"props":2250,"children":2251},{},[2252,2257],{"type":38,"tag":53,"props":2253,"children":2255},{"className":2254},[],[2256],{"type":44,"value":2049},{"type":44,"value":2258},": origin or permission policy blocked it.",{"type":38,"tag":78,"props":2260,"children":2261},{},[2262,2268],{"type":38,"tag":53,"props":2263,"children":2265},{"className":2264},[],[2266],{"type":44,"value":2267},"handler_failed",{"type":44,"value":2269},": Zig handler returned an error or invalid JSON.",{"type":38,"tag":78,"props":2271,"children":2272},{},[2273,2279],{"type":38,"tag":53,"props":2274,"children":2276},{"className":2275},[],[2277],{"type":44,"value":2278},"payload_too_large",{"type":44,"value":2280},": request exceeded bridge limits.",{"type":38,"tag":67,"props":2282,"children":2284},{"id":2283},"file-protocol",[2285],{"type":44,"value":2286},"File protocol",{"type":38,"tag":47,"props":2288,"children":2289},{},[2290,2292,2297,2299,2305,2307,2313],{"type":44,"value":2291},"The default directory is ",{"type":38,"tag":53,"props":2293,"children":2295},{"className":2294},[],[2296],{"type":44,"value":58},{"type":44,"value":2298},", resolved against the CLI's CURRENT WORKING DIRECTORY — run ",{"type":38,"tag":53,"props":2300,"children":2302},{"className":2301},[],[2303],{"type":44,"value":2304},"native automate",{"type":44,"value":2306}," from the app project's directory (where the app was launched). The dir is created by the running app, never by the CLI: a command sent from the wrong cwd fails loudly (",{"type":38,"tag":53,"props":2308,"children":2310},{"className":2309},[],[2311],{"type":44,"value":2312},"error: no automation dir at \u003Cabs path>",{"type":44,"value":2314},") instead of queueing into a dir no app reads, and every queued command prints the absolute dir it wrote to — check that line when a command seems to do nothing.",{"type":38,"tag":47,"props":2316,"children":2317},{},[2318],{"type":44,"value":2319},"Files:",{"type":38,"tag":74,"props":2321,"children":2322},{},[2323,2435,2446,2473,2484],{"type":38,"tag":78,"props":2324,"children":2325},{},[2326,2331,2333,2338,2340,2346,2348,2354,2356,2362,2364,2370,2372,2378,2380,2386,2388,2394,2396,2402,2404,2410,2412,2418,2419,2425,2427,2433],{"type":38,"tag":53,"props":2327,"children":2329},{"className":2328},[],[2330],{"type":44,"value":1184},{"type":44,"value":2332},": app name, readiness, source kind, source size, window metadata, accessibility summary. The ",{"type":38,"tag":53,"props":2334,"children":2336},{"className":2335},[],[2337],{"type":44,"value":88},{"type":44,"value":2339}," line also carries ",{"type":38,"tag":53,"props":2341,"children":2343},{"className":2342},[],[2344],{"type":44,"value":2345},"protocol=\u003Cn>",{"type":44,"value":2347}," (the CLI\u002Fapp handshake: the CLI refuses snapshots — and command queues to a live app — whose protocol version is not its own, naming both versions; the fix is rebuilding whichever binary is stale and comparing ",{"type":38,"tag":53,"props":2349,"children":2351},{"className":2350},[],[2352],{"type":44,"value":2353},"native version",{"type":44,"value":2355},"), ",{"type":38,"tag":53,"props":2357,"children":2359},{"className":2358},[],[2360],{"type":44,"value":2361},"dispatch_errors=\u003Ctotal>",{"type":44,"value":2363}," and ",{"type":38,"tag":53,"props":2365,"children":2367},{"className":2366},[],[2368],{"type":44,"value":2369},"dropped_trace_records=\u003Ctotal>",{"type":44,"value":2371},", and recent degraded handler\u002Fupdate errors appear as ",{"type":38,"tag":53,"props":2373,"children":2375},{"className":2374},[],[2376],{"type":44,"value":2377},"  error event=\u003Ctag> name=\u003CErrorName> timestamp_ns=...",{"type":44,"value":2379}," lines — a handler error no longer exits the app, so grep these to notice one happened. While ",{"type":38,"tag":53,"props":2381,"children":2383},{"className":2382},[],[2384],{"type":44,"value":2385},"profile on",{"type":44,"value":2387}," is active, a ",{"type":38,"tag":53,"props":2389,"children":2391},{"className":2390},[],[2392],{"type":44,"value":2393},"frame_profile \u003Cstage>_p50_us=... \u003Cstage>_p90_us=... \u003Cstage>_max_us=... \u003Cstage>_n=...",{"type":44,"value":2395}," line follows the header with per-stage frame timing. The header also carries ",{"type":38,"tag":53,"props":2397,"children":2399},{"className":2398},[],[2400],{"type":44,"value":2401},"markup_watch=armed|off",{"type":44,"value":2403}," — whether the markup hot-reload watch is armed (armed only in builds where the app wired ",{"type":38,"tag":53,"props":2405,"children":2407},{"className":2406},[],[2408],{"type":44,"value":2409},".markup",{"type":44,"value":2411}," with a ",{"type":38,"tag":53,"props":2413,"children":2415},{"className":2414},[],[2416],{"type":44,"value":2417},"watch_path",{"type":44,"value":2363},{"type":38,"tag":53,"props":2420,"children":2422},{"className":2421},[],[2423],{"type":44,"value":2424},"io",{"type":44,"value":2426},", or registered compiled fragments through ",{"type":38,"tag":53,"props":2428,"children":2430},{"className":2429},[],[2431],{"type":44,"value":2432},"fragment_watch",{"type":44,"value":2434}," — i.e. Debug dev builds).",{"type":38,"tag":78,"props":2436,"children":2437},{},[2438,2444],{"type":38,"tag":53,"props":2439,"children":2441},{"className":2440},[],[2442],{"type":44,"value":2443},"windows.txt",{"type":44,"value":2445},": window list.",{"type":38,"tag":78,"props":2447,"children":2448},{},[2449,2455,2457,2463,2465,2471],{"type":38,"tag":53,"props":2450,"children":2452},{"className":2451},[],[2453],{"type":44,"value":2454},"command.txt",{"type":44,"value":2456},": command input written by CLI and consumed by runtime. The slot is single-entry and the app consumes one command per presented frame: ",{"type":38,"tag":53,"props":2458,"children":2460},{"className":2459},[],[2461],{"type":44,"value":2462},"native automate \u003Ccommand>",{"type":44,"value":2464}," WAITS for the running app to consume it and prints ",{"type":38,"tag":53,"props":2466,"children":2468},{"className":2467},[],[2469],{"type":44,"value":2470},"delivered \u003Caction> -> \u003Cdir>",{"type":44,"value":2472}," on success — it refuses\u002Ffails loudly instead of silently overwriting an unconsumed command, and exits non-zero if the app never consumes it.",{"type":38,"tag":78,"props":2474,"children":2475},{},[2476,2482],{"type":38,"tag":53,"props":2477,"children":2479},{"className":2478},[],[2480],{"type":44,"value":2481},"bridge-response.txt",{"type":44,"value":2483},": last bridge response.",{"type":38,"tag":78,"props":2485,"children":2486},{},[2487,2492,2494,2499,2501,2506],{"type":38,"tag":53,"props":2488,"children":2490},{"className":2489},[],[2491],{"type":44,"value":1699},{"type":44,"value":2493},": deterministic reference-rendered PNG of a ",{"type":38,"tag":53,"props":2495,"children":2497},{"className":2496},[],[2498],{"type":44,"value":133},{"type":44,"value":2500}," view, written by the ",{"type":38,"tag":53,"props":2502,"children":2504},{"className":2503},[],[2505],{"type":44,"value":163},{"type":44,"value":2507}," command.",{"type":38,"tag":47,"props":2509,"children":2510},{},[2511,2513,2518,2520,2526],{"type":44,"value":2512},"The runtime polls ",{"type":38,"tag":53,"props":2514,"children":2516},{"className":2515},[],[2517],{"type":44,"value":2454},{"type":44,"value":2519},". After processing a command, it writes ",{"type":38,"tag":53,"props":2521,"children":2523},{"className":2522},[],[2524],{"type":44,"value":2525},"done",{"type":44,"value":90},{"type":38,"tag":67,"props":2528,"children":2530},{"id":2529},"debugging-automation-failures",[2531],{"type":44,"value":2532},"Debugging automation failures",{"type":38,"tag":47,"props":2534,"children":2535},{},[2536,2538,2543,2545,2550],{"type":44,"value":2537},"If ",{"type":38,"tag":53,"props":2539,"children":2541},{"className":2540},[],[2542],{"type":44,"value":1343},{"type":44,"value":2544}," times out with NO snapshot file at all, it prints a teaching error naming the automation dir it watched and pointing at ",{"type":38,"tag":53,"props":2546,"children":2548},{"className":2547},[],[2549],{"type":44,"value":199},{"type":44,"value":2551}," and the working directory — start from that message. Otherwise:",{"type":38,"tag":1325,"props":2553,"children":2554},{},[2555,2560,2571,2590,2602,2614],{"type":38,"tag":78,"props":2556,"children":2557},{},[2558],{"type":44,"value":2559},"Confirm the app is still running.",{"type":38,"tag":78,"props":2561,"children":2562},{},[2563,2565,2570],{"type":44,"value":2564},"Confirm it was built with ",{"type":38,"tag":53,"props":2566,"children":2568},{"className":2567},[],[2569],{"type":44,"value":199},{"type":44,"value":90},{"type":38,"tag":78,"props":2572,"children":2573},{},[2574,2576,2581,2583,2589],{"type":44,"value":2575},"Confirm the runner passes ",{"type":38,"tag":53,"props":2577,"children":2579},{"className":2578},[],[2580],{"type":44,"value":4},{"type":44,"value":2582}," into ",{"type":38,"tag":53,"props":2584,"children":2586},{"className":2585},[],[2587],{"type":44,"value":2588},"Runtime.init",{"type":44,"value":90},{"type":38,"tag":78,"props":2591,"children":2592},{},[2593,2595,2601],{"type":44,"value":2594},"Check ",{"type":38,"tag":53,"props":2596,"children":2598},{"className":2597},[],[2599],{"type":44,"value":2600},".zig-cache\u002Fnative-sdk-automation\u002Fsnapshot.txt",{"type":44,"value":90},{"type":38,"tag":78,"props":2603,"children":2604},{},[2605,2607,2612],{"type":44,"value":2606},"Delete stale files in ",{"type":38,"tag":53,"props":2608,"children":2610},{"className":2609},[],[2611],{"type":44,"value":58},{"type":44,"value":2613}," and restart the app.",{"type":38,"tag":78,"props":2615,"children":2616},{},[2617,2619,2625],{"type":44,"value":2618},"Run with more tracing, for example ",{"type":38,"tag":53,"props":2620,"children":2622},{"className":2621},[],[2623],{"type":44,"value":2624},"zig build run -Dtrace=all",{"type":44,"value":90},{"type":38,"tag":47,"props":2627,"children":2628},{},[2629,2631,2636,2638,2644,2646,2651],{"type":44,"value":2630},"If the CLI reports an automation protocol mismatch (or a snapshot with no protocol version): the ",{"type":38,"tag":53,"props":2632,"children":2634},{"className":2633},[],[2635],{"type":44,"value":399},{"type":44,"value":2637}," binary and the app were built from different framework versions — rebuild the older side (stale ",{"type":38,"tag":53,"props":2639,"children":2641},{"className":2640},[],[2642],{"type":44,"value":2643},"zig-out\u002Fbin",{"type":44,"value":2645}," copies of the CLI are the classic cause; ",{"type":38,"tag":53,"props":2647,"children":2649},{"className":2648},[],[2650],{"type":44,"value":2353},{"type":44,"value":2652}," prints the commit the binary was built from).",{"type":38,"tag":47,"props":2654,"children":2655},{},[2656,2658,2663],{"type":44,"value":2657},"Stale INSTANCES are called out too: automate verbs print a LOUD warning when the publishing app's process started BEFORE the newest binary in ",{"type":38,"tag":53,"props":2659,"children":2661},{"className":2660},[],[2662],{"type":44,"value":2643},{"type":44,"value":2664}," was built — a leftover instance from an earlier run impersonating the new build. Kill it and relaunch the fresh binary before trusting any snapshot.",{"type":38,"tag":47,"props":2666,"children":2667},{},[2668,2669,2675],{"type":44,"value":2537},{"type":38,"tag":53,"props":2670,"children":2672},{"className":2671},[],[2673],{"type":44,"value":2674},"snapshot",{"type":44,"value":2676}," says no app connected:",{"type":38,"tag":74,"props":2678,"children":2679},{},[2680,2685,2690,2695],{"type":38,"tag":78,"props":2681,"children":2682},{},[2683],{"type":44,"value":2684},"The automation directory may not exist yet.",{"type":38,"tag":78,"props":2686,"children":2687},{},[2688],{"type":44,"value":2689},"The app may be running from a different working directory.",{"type":38,"tag":78,"props":2691,"children":2692},{},[2693],{"type":44,"value":2694},"The app may be built without automation.",{"type":38,"tag":78,"props":2696,"children":2697},{},[2698],{"type":44,"value":2699},"The app may not have reached runtime startup.",{"type":38,"tag":47,"props":2701,"children":2702},{},[2703],{"type":44,"value":2704},"If bridge automation fails:",{"type":38,"tag":74,"props":2706,"children":2707},{},[2708,2713,2718,2729,2734],{"type":38,"tag":78,"props":2709,"children":2710},{},[2711],{"type":44,"value":2712},"Check command name spelling.",{"type":38,"tag":78,"props":2714,"children":2715},{},[2716],{"type":44,"value":2717},"Check app handler registration.",{"type":38,"tag":78,"props":2719,"children":2720},{},[2721,2723,2728],{"type":44,"value":2722},"Check bridge policy origins for ",{"type":38,"tag":53,"props":2724,"children":2726},{"className":2725},[],[2727],{"type":44,"value":2041},{"type":44,"value":90},{"type":38,"tag":78,"props":2730,"children":2731},{},[2732],{"type":44,"value":2733},"Check runtime permissions.",{"type":38,"tag":78,"props":2735,"children":2736},{},[2737],{"type":44,"value":2738},"Check that the handler returns valid JSON.",{"type":38,"tag":67,"props":2740,"children":2742},{"id":2741},"ci-and-smoke-tests",[2743],{"type":44,"value":2744},"CI and smoke tests",{"type":38,"tag":47,"props":2746,"children":2747},{},[2748],{"type":44,"value":2749},"Use automation for minimal integration confidence:",{"type":38,"tag":203,"props":2751,"children":2753},{"className":205,"code":2752,"language":207,"meta":208,"style":208},"zig build test-webview-smoke -Dplatform=macos\n",[2754],{"type":38,"tag":53,"props":2755,"children":2756},{"__ignoreMap":208},[2757],{"type":38,"tag":214,"props":2758,"children":2759},{"class":216,"line":217},[2760,2764,2768,2772],{"type":38,"tag":214,"props":2761,"children":2762},{"style":221},[2763],{"type":44,"value":224},{"type":38,"tag":214,"props":2765,"children":2766},{"style":227},[2767],{"type":44,"value":230},{"type":38,"tag":214,"props":2769,"children":2770},{"style":227},[2771],{"type":44,"value":298},{"type":38,"tag":214,"props":2773,"children":2774},{"style":227},[2775],{"type":44,"value":303},{"type":38,"tag":47,"props":2777,"children":2778},{},[2779],{"type":44,"value":2780},"A good smoke test:",{"type":38,"tag":1325,"props":2782,"children":2783},{},[2784,2802,2807,2812,2824,2835,2840],{"type":38,"tag":78,"props":2785,"children":2786},{},[2787,2789,2794,2795,2801],{"type":44,"value":2788},"Builds an example with ",{"type":38,"tag":53,"props":2790,"children":2792},{"className":2791},[],[2793],{"type":44,"value":199},{"type":44,"value":2363},{"type":38,"tag":53,"props":2796,"children":2798},{"className":2797},[],[2799],{"type":44,"value":2800},"-Djs-bridge=true",{"type":44,"value":90},{"type":38,"tag":78,"props":2803,"children":2804},{},[2805],{"type":44,"value":2806},"Starts the app in a GUI-capable session.",{"type":38,"tag":78,"props":2808,"children":2809},{},[2810],{"type":44,"value":2811},"Waits for readiness.",{"type":38,"tag":78,"props":2813,"children":2814},{},[2815,2817,2822],{"type":44,"value":2816},"Verifies snapshot metadata (",{"type":38,"tag":53,"props":2818,"children":2820},{"className":2819},[],[2821],{"type":44,"value":1001},{"type":44,"value":2823}," with the patterns that matter).",{"type":38,"tag":78,"props":2825,"children":2826},{},[2827,2829,2834],{"type":44,"value":2828},"Sends ",{"type":38,"tag":53,"props":2830,"children":2832},{"className":2831},[],[2833],{"type":44,"value":2077},{"type":44,"value":90},{"type":38,"tag":78,"props":2836,"children":2837},{},[2838],{"type":44,"value":2839},"Exercises builtin windows\u002FWebViews if the app enables them.",{"type":38,"tag":78,"props":2841,"children":2842},{},[2843],{"type":44,"value":2844},"Fails on timeout or unexpected bridge response.",{"type":38,"tag":47,"props":2846,"children":2847},{},[2848,2850,2856,2858,2864,2866,2872,2874,2880,2882,2887,2889,2894],{"type":44,"value":2849},"Apps scaffolded with ",{"type":38,"tag":53,"props":2851,"children":2853},{"className":2852},[],[2854],{"type":44,"value":2855},"native init --full",{"type":44,"value":2857}," ship this as ",{"type":38,"tag":53,"props":2859,"children":2861},{"className":2860},[],[2862],{"type":44,"value":2863},".github\u002Fworkflows\u002Fci.yml",{"type":44,"value":2865}," (the zero-config default scaffold skips CI): a null-platform ",{"type":38,"tag":53,"props":2867,"children":2869},{"className":2868},[],[2870],{"type":44,"value":2871},"zig build test",{"type":44,"value":2873}," job plus a Linux Xvfb smoke job that launches the binary, runs ",{"type":38,"tag":53,"props":2875,"children":2877},{"className":2876},[],[2878],{"type":44,"value":2879},"automate wait",{"type":44,"value":2881},", asserts on the snapshot with ",{"type":38,"tag":53,"props":2883,"children":2885},{"className":2884},[],[2886],{"type":44,"value":1001},{"type":44,"value":2888},", and checks a non-empty ",{"type":38,"tag":53,"props":2890,"children":2892},{"className":2891},[],[2893],{"type":44,"value":1983},{"type":44,"value":2895}," artifact. Extend that file rather than writing grep chains by hand.",{"type":38,"tag":47,"props":2897,"children":2898},{},[2899],{"type":44,"value":2900},"Do not use automation for exhaustive UI testing. It is a runtime and bridge smoke layer.",{"type":38,"tag":67,"props":2902,"children":2904},{"id":2903},"notes",[2905],{"type":44,"value":2906},"Notes",{"type":38,"tag":74,"props":2908,"children":2909},{},[2910,2922,2934,2939],{"type":38,"tag":78,"props":2911,"children":2912},{},[2913,2915,2920],{"type":44,"value":2914},"Automation is compile-time gated: apps built without ",{"type":38,"tag":53,"props":2916,"children":2918},{"className":2917},[],[2919],{"type":44,"value":199},{"type":44,"value":2921}," ignore automation files.",{"type":38,"tag":78,"props":2923,"children":2924},{},[2925,2927,2932],{"type":44,"value":2926},"Screenshots cover retained-canvas (",{"type":38,"tag":53,"props":2928,"children":2930},{"className":2929},[],[2931],{"type":44,"value":133},{"type":44,"value":2933},") views only; WebView pixels are not captured.",{"type":38,"tag":78,"props":2935,"children":2936},{},[2937],{"type":44,"value":2938},"WebView DOM interaction is intentionally out of scope for this file-based automation layer.",{"type":38,"tag":78,"props":2940,"children":2941},{},[2942,2943,2949],{"type":44,"value":1407},{"type":38,"tag":53,"props":2944,"children":2946},{"className":2945},[],[2947],{"type":44,"value":2948},"native skills get core --full",{"type":44,"value":2950}," for app architecture, bridge policy, packaging, and debugging context.",{"type":38,"tag":2952,"props":2953,"children":2954},"style",{},[2955],{"type":44,"value":2956},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"items":2958,"total":360},[2959,2965,2980,2994,3007],{"slug":4,"name":4,"fn":5,"description":6,"org":2960,"tags":2961,"stars":21,"repoUrl":22,"updatedAt":23},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2962,2963,2964],{"name":13,"slug":4,"type":14},{"name":16,"slug":17,"type":14},{"name":19,"slug":20,"type":14},{"slug":2966,"name":2966,"fn":2967,"description":2968,"org":2969,"tags":2970,"stars":21,"repoUrl":22,"updatedAt":2979},"native-sdk","develop native desktop applications with Zig","Discovery skill for the Native SDK, the complete toolkit for building native desktop applications - views are declarative Native markup (.native), logic is plain Zig, and the toolkit's own engine renders every pixel, with WebView surfaces as the optional web-content path. Use when the user asks what the Native SDK is, how to build a Native SDK app, author native UI, scaffold an app, configure app.zon, add bridge commands, embed web content, package an app, test a running app, or automate a Native SDK app.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2971,2974,2977],{"name":2972,"slug":2973,"type":14},"Desktop","desktop",{"name":2975,"slug":2976,"type":14},"SDK","sdk",{"name":2978,"slug":224,"type":14},"Zig","2026-07-17T06:08:49.453123",{"slug":2981,"name":2981,"fn":2982,"description":2983,"org":2984,"tags":2985,"stars":21,"repoUrl":22,"updatedAt":2993},"native-ui","build native user interfaces with Zig","Authoring guide for native-rendered Native SDK apps - declarative Native markup (.native) views plus Zig logic on the UiApp loop. Use when building or modifying native UI (widgets, layout, bindings, messages), writing .native files, wiring Model\u002FMsg\u002Fupdate, testing markup views, or verifying a native app through the automation harness.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2986,2987,2989,2992],{"name":2972,"slug":2973,"type":14},{"name":2988,"slug":399,"type":14},"Native",{"name":2990,"slug":2991,"type":14},"UI Components","ui-components",{"name":2978,"slug":224,"type":14},"2026-07-26T05:49:56.17769",{"slug":2995,"name":2995,"fn":2996,"description":2997,"org":2998,"tags":2999,"stars":21,"repoUrl":22,"updatedAt":3006},"ts-core","author TypeScript application cores","Authoring guide for TypeScript app cores - Model, Msg, update, and the pure functions they call, written in the closed app-core subset and compiled ahead-of-time to arena-backed Zig by the @native-sdk\u002Fcore transpiler. Use when writing or modifying a src\u002Fcore.ts app core, fixing subset checker errors (NS1001-NS1060), or deciding how to express state, messages, text (bytes and the byte-text string methods), text input, continuous controls (sliders, scroll), effects (Cmd), subscriptions (Sub), the host-event wiring channels (frameMsg, keyMsg, appearanceMsg, chromeMsg, envMsgs, app.zon assets), derived values, the view_unbound lint opt-out, local mutation of owned arrays, or how to split a core into modules under src\u002F (relative imports, namespace imports, @native-sdk\u002Fcore\u002Ftext, @native-sdk\u002Fcore\u002Fevents).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3000,3003],{"name":3001,"slug":3002,"type":14},"Engineering","engineering",{"name":3004,"slug":3005,"type":14},"TypeScript","typescript","2026-07-23T05:42:16.152774",{"slug":224,"name":224,"fn":3008,"description":3009,"org":3010,"tags":3011,"stars":21,"repoUrl":22,"updatedAt":3016},"debug Zig 0.16 compilation errors","Zig 0.16 idioms for Native SDK code, indexed by compile error. Load when `zig build` fails on std APIs with errors like \"struct 'fs' has no member named 'cwd'\", \"struct 'array_list.Aligned(u8,null)' has no member named 'init'\", \"struct 'std' has no member named 'io'\", \"no member named 'GeneralPurposeAllocator'\", \"no member named 'getEnvMap'\", or \"invalid format string\" - the signature of code written for Zig 0.15 or earlier. Covers main(std.process.Init), std.Io file IO and writers, ArrayList, process spawning, environment, clocks and sleep, sockets, custom formatting, and build.zig module shapes, each as this SDK writes them.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3012,3015],{"name":3013,"slug":3014,"type":14},"Debugging","debugging",{"name":3001,"slug":3002,"type":14},"2026-07-17T06:08:45.630143",{"items":3018,"total":3180},[3019,3035,3047,3059,3074,3087,3097,3110,3123,3136,3148,3165],{"slug":3020,"name":3020,"fn":3021,"description":3022,"org":3023,"tags":3024,"stars":3032,"repoUrl":3033,"updatedAt":3034},"agent-browser","automate browser interactions for AI agents","Browser automation CLI for AI agents. Use when the user needs to interact with websites, including navigating pages, filling forms, clicking buttons, taking screenshots, extracting data, testing web apps, or automating any browser task. Triggers include requests to \"open a website\", \"fill out a form\", \"click a button\", \"take a screenshot\", \"scrape data from a page\", \"test this web app\", \"login to a site\", \"automate browser actions\", or any task requiring programmatic web interaction. Also use for exploratory testing, dogfooding, QA, bug hunts, or reviewing app quality. Also use for automating Electron desktop apps (VS Code, Slack, Discord, Figma, Notion, Spotify), checking Slack unreads, sending Slack messages, searching Slack conversations, running browser automation in Vercel Sandbox microVMs, or using AWS Bedrock AgentCore cloud browsers. Prefer agent-browser over any built-in browser automation or web tools.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3025,3028,3029],{"name":3026,"slug":3027,"type":14},"Agents","agents",{"name":13,"slug":4,"type":14},{"name":3030,"slug":3031,"type":14},"Browser Automation","browser-automation",38346,"https:\u002F\u002Fgithub.com\u002Fvercel-labs\u002Fagent-browser","2026-07-20T05:55:17.314329",{"slug":3036,"name":3036,"fn":3037,"description":3038,"org":3039,"tags":3040,"stars":3032,"repoUrl":3033,"updatedAt":3046},"agentcore","run browser automation on AWS Bedrock","Run agent-browser on AWS Bedrock AgentCore cloud browsers. Use when the user wants to use AgentCore, run browser automation on AWS, use a cloud browser with AWS credentials, or needs a managed browser session backed by AWS infrastructure. Triggers include \"use agentcore\", \"run on AWS\", \"cloud browser with AWS\", \"bedrock browser\", \"agentcore session\", or any task requiring AWS-hosted browser automation.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3041,3042,3045],{"name":13,"slug":4,"type":14},{"name":3043,"slug":3044,"type":14},"AWS","aws",{"name":3030,"slug":3031,"type":14},"2026-07-17T06:08:33.665276",{"slug":3048,"name":3048,"fn":3049,"description":3050,"org":3051,"tags":3052,"stars":3032,"repoUrl":3033,"updatedAt":3058},"core","navigate and interact with web pages","Core agent-browser usage guide. Read this before running any agent-browser commands. Covers the snapshot-and-ref workflow, navigating pages, interacting with elements (click, fill, type, select), extracting text and data, taking screenshots, managing tabs, handling forms and auth, waiting for content, running multiple browser sessions in parallel, and troubleshooting common failures. Use when the user asks to interact with a website, fill a form, click something, extract data, take a screenshot, log into a site, test a web app, or automate any browser task.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3053,3054,3055],{"name":3026,"slug":3027,"type":14},{"name":3030,"slug":3031,"type":14},{"name":3056,"slug":3057,"type":14},"Navigation","navigation","2026-07-26T05:47:42.378419",{"slug":3060,"name":3060,"fn":3061,"description":3062,"org":3063,"tags":3064,"stars":3032,"repoUrl":3033,"updatedAt":3073},"derive-client","reverse engineer internal APIs from browser traffic","Reverse-engineer a website's internal API by recording browser traffic into a HAR file, then generate a standalone client or CLI that calls the endpoints directly, with no browser needed after the first recording. Use when asked to \"derive a client\", \"build a CLI for \u003Csite>\", \"reverse engineer this site's API\", \"record network requests\", \"turn this site into an API\", or when the same site will be automated repeatedly and direct HTTP calls would beat driving the browser every time.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3065,3068,3069,3070],{"name":3066,"slug":3067,"type":14},"API Development","api-development",{"name":13,"slug":4,"type":14},{"name":3030,"slug":3031,"type":14},{"name":3071,"slug":3072,"type":14},"Web Scraping","web-scraping","2026-07-20T06:24:11.928835",{"slug":3075,"name":3075,"fn":3076,"description":3077,"org":3078,"tags":3079,"stars":3032,"repoUrl":3033,"updatedAt":3086},"dogfood","perform exploratory testing on web applications","Systematically explore and test a web application to find bugs, UX issues, and other problems. Use when asked to \"dogfood\", \"QA\", \"exploratory test\", \"find issues\", \"bug hunt\", \"test this app\u002Fsite\u002Fplatform\", or review the quality of a web application. Produces a structured report with full reproduction evidence -- step-by-step screenshots, repro videos, and detailed repro steps for every issue -- so findings can be handed directly to the responsible teams.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3080,3081,3082,3085],{"name":3030,"slug":3031,"type":14},{"name":3013,"slug":3014,"type":14},{"name":3083,"slug":3084,"type":14},"QA","qa",{"name":19,"slug":20,"type":14},"2026-07-17T06:07:41.421482",{"slug":3088,"name":3088,"fn":3089,"description":3090,"org":3091,"tags":3092,"stars":3032,"repoUrl":3033,"updatedAt":3096},"electron","automate Electron desktop applications","Automate Electron desktop apps (VS Code, Slack, Discord, Figma, Notion, Spotify, etc.) using agent-browser via Chrome DevTools Protocol. Use when the user needs to interact with an Electron app, automate a desktop app, connect to a running app, control a native app, or test an Electron application. Triggers include \"automate Slack app\", \"control VS Code\", \"interact with Discord app\", \"test this Electron app\", \"connect to desktop app\", or any task requiring automation of a native Electron application.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3093,3094,3095],{"name":3026,"slug":3027,"type":14},{"name":3030,"slug":3031,"type":14},{"name":2972,"slug":2973,"type":14},"2026-07-17T06:08:28.007783",{"slug":3098,"name":3098,"fn":3099,"description":3100,"org":3101,"tags":3102,"stars":3032,"repoUrl":3033,"updatedAt":3109},"slack","interact with Slack workspaces","Interact with Slack workspaces using browser automation. Use when the user needs to check unread channels, navigate Slack, send messages, extract data, find information, search conversations, or automate any Slack task. Triggers include \"check my Slack\", \"what channels have unreads\", \"send a message to\", \"search Slack for\", \"extract from Slack\", \"find who said\", or any task requiring programmatic Slack interaction.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3103,3104,3107],{"name":3030,"slug":3031,"type":14},{"name":3105,"slug":3106,"type":14},"Messaging","messaging",{"name":3108,"slug":3098,"type":14},"Slack","2026-07-17T06:08:27.679015",{"slug":3111,"name":3111,"fn":3112,"description":3113,"org":3114,"tags":3115,"stars":3032,"repoUrl":3033,"updatedAt":3122},"vercel-sandbox","run browser automation in Vercel Sandbox","Run agent-browser + Chrome inside Vercel Sandbox microVMs for browser automation from any Vercel-deployed app. Use when the user needs browser automation in a Vercel app (Next.js, SvelteKit, Nuxt, Remix, Astro, etc.), wants to run headless Chrome without binary size limits, needs persistent browser sessions across commands, or wants ephemeral isolated browser environments. Triggers include \"Vercel Sandbox browser\", \"microVM Chrome\", \"agent-browser in sandbox\", \"browser automation on Vercel\", or any task requiring Chrome in a Vercel Sandbox.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3116,3117,3118,3119],{"name":13,"slug":4,"type":14},{"name":3030,"slug":3031,"type":14},{"name":19,"slug":20,"type":14},{"name":3120,"slug":3121,"type":14},"Vercel","vercel","2026-07-17T06:08:28.349899",{"slug":3124,"name":3124,"fn":3125,"description":3126,"org":3127,"tags":3128,"stars":3133,"repoUrl":3134,"updatedAt":3135},"deploy-to-vercel","deploy applications to Vercel","Deploy applications and websites to Vercel. Use when the user requests deployment actions like \"deploy my app\", \"deploy and give me the link\", \"push this live\", or \"create a preview deployment\".",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3129,3132],{"name":3130,"slug":3131,"type":14},"Deployment","deployment",{"name":3120,"slug":3121,"type":14},28993,"https:\u002F\u002Fgithub.com\u002Fvercel-labs\u002Fagent-skills","2026-07-17T06:08:41.18374",{"slug":3137,"name":3137,"fn":3138,"description":3139,"org":3140,"tags":3141,"stars":3133,"repoUrl":3134,"updatedAt":3147},"vercel-cli-with-tokens","manage Vercel projects via CLI","Deploy and manage projects on Vercel using token-based authentication. Use when working with Vercel CLI using access tokens rather than interactive login — e.g. \"deploy to vercel\", \"set up vercel\", \"add environment variables to vercel\".",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3142,3145,3146],{"name":3143,"slug":3144,"type":14},"CLI","cli",{"name":3130,"slug":3131,"type":14},{"name":3120,"slug":3121,"type":14},"2026-07-17T06:08:41.84179",{"slug":3149,"name":3149,"fn":3150,"description":3151,"org":3152,"tags":3153,"stars":3133,"repoUrl":3134,"updatedAt":3164},"vercel-composition-patterns","implement scalable React composition patterns","React composition patterns that scale. Use when refactoring components with boolean prop proliferation, building flexible component libraries, or designing reusable APIs. Triggers on tasks involving compound components, render props, context providers, or component architecture. Includes React 19 API changes.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3154,3157,3160,3163],{"name":3155,"slug":3156,"type":14},"Best Practices","best-practices",{"name":3158,"slug":3159,"type":14},"Frontend","frontend",{"name":3161,"slug":3162,"type":14},"React","react",{"name":2990,"slug":2991,"type":14},"2026-07-17T06:05:40.576913",{"slug":3166,"name":3166,"fn":3167,"description":3168,"org":3169,"tags":3170,"stars":3133,"repoUrl":3134,"updatedAt":3179},"vercel-optimize","optimize Vercel project performance and costs","Use for Vercel cost and performance optimization on deployed projects, especially Next.js, SvelteKit, Nuxt, and limited Astro apps. Collect Vercel metrics, usage, project config, and code scan results first; investigate only metric-backed candidates; produce ranked recommendations grounded in verified files and version-aware Vercel\u002Fframework docs. Trigger for Vercel bill reduction, slow or expensive routes, caching opportunities, Function Invocations, Build Minutes, Fast Data Transfer, Core Web Vitals, Bot Management, Fluid compute, or cost breakdown requests.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3171,3174,3175,3178],{"name":3172,"slug":3173,"type":14},"Cost Optimization","cost-optimization",{"name":3130,"slug":3131,"type":14},{"name":3176,"slug":3177,"type":14},"Performance","performance",{"name":3120,"slug":3121,"type":14},"2026-07-17T06:04:08.327515",100]