# 0.3.0

Released 24 October 2026.

## Desktop app

- **New**: save the app's current screen and data from the device panel's Capture menu, and load it back in under a second
- **New**: a Tunings screen shows what `squiggle flow --tune` and `--repair` measured and what they propose
- **New**: Performance has a Probes section: write a rule, install it on the connected app, and see when it held
- **New**: Product has a State section, showing what your app held on each screen
- **New**: Analytics names the control each touch landed on, and what each drag was meant to do
- **New**: the tour has 25 chapters, folds its sections and shows how much you've read. Every chapter has a hint
- **New**: menu items show their keyboard shortcut, such as Show all devices (⌘G)
- **New**: Settings are grouped into sections, and the simulator stays in view while you change them
- **New**: a healed step shows how close the match was, and warns when it was close to failing
- **Fix**: Studio checks for updates on every launch, and Check for Updates opens the update card
- **Fix**: Studio's Replay goes to the flow's start screen first, as `squiggle flow` does
- **Fix**: zooming into a live timeline no longer jumps when the session grows, and a timeline parked at the end follows it
- **Fix**: hover cards on graphs stay above everything and inside the window, and close with the session they describe
- **Fix**: the inspector's selection box stays inside the device screen and follows a scroll
- **Fix**: the Renders table keeps the Component column in view while it scrolls sideways
- **Fix**: a popover over the device preview scrolls instead of the device
- **Fix**: Studio no longer reloads an app that another tool is driving
- **Fix**: Studio removes a recent project whose folder is gone
- **Perf**: Studio's JS thread idles with an app connected: live performance analysis reads only what changed

## CLI

- **Breaking**: Squiggle saves an app's runs, recordings and performance data only into the project that owns it: the app is listed under `apps` in `.squiggle/config.jsonc`, or its development build was built inside the project. Any other app still works live, but nothing about it is saved. If an app's data stops appearing, add it to `apps`
- **Breaking**: with more than one simulator booted, a command that drives a device refuses to pick one and lists them (`E_DEVICE_AMBIGUOUS`). Name one with `--udid`, `SQUIGGLE_UDID` or `udid` in your config. Your agent's tools refuse the same way
- **New**: `squiggle card` checks whether a change is done: it runs the flows that reach the screens you changed, checks what they measured, and lists each problem with the line to fix
- **New**: `squiggle add screen` writes a new screen from one of six templates, with a flow that drives it and a renders budget it meets
- **New**: `squiggle save` and `squiggle load` keep the screen the app is on, with the data behind it, and put it back in under a second instead of tapping through screens
- **New**: `squiggle bisect` finds the commit that slowed a flow, without rebuilding the app. `--hunks` says how much of the slowdown each change in that commit accounts for
- **New**: `squiggle bakeoff` runs one flow against several revisions and compares pass rate, duration, renders, dropped frames, memory growth and startup in one table
- **New**: `squiggle motion` records how a view moves after a tap and gives you the `withSpring` config that reproduces it
- **New**: `squiggle contracts propose` turns what your passing runs always showed into asserts in your flows, and proposes removing an assert that stopped holding
- **New**: `squiggle perf budget` checks each screen's budget against the newest session, with the margin to each limit. `--changed` checks only the screens your branch changed
- **New**: `squiggle perf labels` exports every run with its outcome, heals and app state, as JSONL
- **New**: `squiggle probe when` runs a rule inside your app and reports when it held. `squiggle probe export` keeps the rule as a detector
- **New**: `squiggle store audit` lists data in your project that came from an app the project doesn't own. `--purge` removes it
- **New**: the first `squiggle up` in a project saves a baseline named `first`
- **Fix**: `squiggle up` gets a development build past the Expo launcher, and reports ready only once the app's JS has connected
- **Fix**: every command uses the port from your config or `SQUIGGLE_PORT`, not always 7777
- **Fix**: a YAML syntax error in a flow is reported as one, not as "a bug in Squiggle"
- **Fix**: a Ctrl-C during `--matrix` or `--repair` puts the simulator's appearance and text size back
- **Fix**: a `squiggle bench` series with a failed run records the failed run and exits 1

## Agents & MCP

- **New**: `report_card` gives your agent the report card
- **New**: `bisect_state` finds the store values that make a flow fail, from two save slots
- **New**: `what_if` tells your agent what a flow did under a repeated tap, a Back press or a bad network, from runs already recorded, without touching the app
- **New**: `tune` lists and sets the values your app declared tunable
- **New**: `product_state` shows what your app held on each screen
- **New**: `branch` can start from a save slot, and tries the likeliest alternative first
- **New**: `propose_fix` turns a `--tune` or `--repair` result into a code change
- **Fix**: an idle `squiggle mcp` no longer uses a quarter of a CPU core
- **Fix**: `launch_app` gets past the development launcher and an "Open in …?" alert, and returns once the first screen is up

## Flows & device

- **New**: flows drive the photo picker, the document picker, the share sheet and permission prompts that iOS shows over your app. Select their controls by the text they show
- **New**: `squiggle flow --twin` runs a flow on a working network and again on a slow, flaky, cut-off or offline one, and reports what your app did differently, such as a spinner that never goes away or an empty state shown for a server error
- **New**: `--fuzz` breaks each endpoint the flow called, one at a time
- **New**: `--race` finds a repeated tap or a Back press, made while a request is in flight, that sends a change twice or navigates after the user left. It writes the shortest flow that reproduces each
- **New**: `--chaos` runs a flow on a bad network, and `--stubs` answers the network from an earlier run. A flow can declare `chaos` in its config
- **New**: `--cache-twins <cold>,<warm>` shows which requests a warm app answered from its cache and which it fetched again
- **New**: `--bisect-state <green>,<failing>` finds the few store values that make a flow fail, and names the lines that write them and the components that read them
- **New**: `--matrix` runs a flow in each simulator setup, such as dark mode and the largest text size, and lists the layout problems each one adds. `--repair` searches the values you declared tunable for a layout with no problem in any setup
- **New**: `--tune <name>` picks a tunable value, such as a list's `windowSize`, by measuring dropped frames at each one
- **New**: `--cliff VAR=lo..hi` finds the list size where dropped frames start to climb, and what rendered most there
- **New**: `--boundaries` checks the edges of every control and reports an edge whose touches land on something else
- **New**: `--ax-only` finds every control through the accessibility tree alone, the way VoiceOver reaches it
- **New**: three asserts: `scroll` (frame rate, dropped frames, blank cells), `smooth` (how a region moves through a transition) and `memoryDelta` (memory growth since the previous step). Thresholds you leave out come from the screen's budget
- **New**: `waitFor` waits for a control to appear or go away, so opening a sheet needs no retry
- **New**: `swipe` takes a `duration` to drag slowly, and a `hold` to long-press before dragging, so a flow can reorder a list
- **New**: `softKeyboard: true` keeps the on-screen keyboard up while a flow types
- **New**: a near miss says which part of the selector scored what, and how far under the threshold it fell
- **Fix**: a selector with a `testID` and `text` needs both to match
- **Fix**: a `type` step waits for the text to land in the field, and checks it
- **Fix**: a control whose `testID`, label or press handler changes in place is found under its new name
- **Fix**: a scroll longer than its list is several drags inside the list
- **Fix**: a held drag starts on the control it holds
- **Perf**: the wait before each step is 41% shorter, and a relaunch finishes as soon as the app shows its screen

## Performance

- **New**: set a performance budget per screen in your config
- **New**: startup is split into phases, each timed by the module that owns it
- **New**: a JS stall comes with its CPU profile, including the first stall after a fresh launch
- **New**: a wait that runs long names the component still animating, with its file and line
- **New**: the `js-driven-motion` finding catches an animation that moves only while JS runs
- **Fix**: capturing a stall profile can no longer crash your app
- **Fix**: one app launch is one session, whether Studio, the CLI or both watch it
- **Fix**: samples follow a relaunched app into its new session
- **Fix**: a run that opens the performance store while another is writing to it no longer loses its session
- **Fix**: a long task that repeats is one finding per cause, grouped across sessions

## Product analytics

- **New**: split any report by a state sessions reached, with `--where`
- **New**: see which app states friction happens in, the states real sessions reach that no flow tests, and how states drift across releases
- **New**: an abandoned drag is a friction finding

## SDK

- **New**: declare values Squiggle may tune, such as a list's `windowSize`, with `defineTunable` and `useTunable`
- **New**: save slots capture your Zustand, Legend State and MMKV stores without setup
- **New**: state analytics samples what your app held on each screen, as shapes rather than values. It needs a build variable and the user's consent, set with `setStateAnalyticsConsent`
- **Fix**: gestures in your app run as worklets again with the published SDK
- **Fix**: upgrading Squiggle no longer needs `--clear` on Metro
- **Fix**: Zustand stores appear under their own names, such as `useCart`
- **Fix**: a slow drag keeps its interaction until the finger lifts

## Website

- **New**: every docs and changelog page has a Markdown copy at its URL plus `.md`, and each docs page has Copy as Markdown and View as Markdown, for your coding agent
- **New**: the templates page runs each template on the demo device and shows its report card
- **New**: on a phone, the menu and the search can be dragged away
