Run settings

For AI agents: this page is Markdown at https://squiggle.sh/docs/run/settings.md. Every docs page is listed at https://squiggle.sh/llms.txt, and all of them are one file at https://squiggle.sh/llms-full.txt.

Flags change how one run delivers steps and how long it waits. A flow that always needs a setting declares it in its own config block, so nobody has to remember the flag.

Flags

FlagWhat it does
--direct-scrollMakes mode: direct the default for scrollUntilVisible. Much faster; skips momentum, paging and snapping.
--auto-scrollScrolls an off-screen control into view instead of failing the step.
--no-fast-settleWaits the full fixed time before each step, instead of moving on when the app stops moving.
--no-in-processSends taps, text and swipes as real touches from outside the app. Slower; use it when the touch handling itself is what you test.
--no-animationsTurns iOS animations off in the app, for runs that must be identical.
--capture-netLets the flow wait for the network and stub requests.
--strict-assertFails the run when a step couldn’t be performed at all.
--chaos <profile>Runs the flow on a bad network. See below.
--stubs <run>Answers the network from an earlier run. See below.
--matrixRuns the flow once in each simulator setup and lists the layout problems each one adds. See below.
--ax-onlyFinds every control through the accessibility tree only, the way VoiceOver reaches it. See below.
--settle-debugPrints every wait under the step it belongs to. When a wait runs long, it names what was still animating: the component and its file:line.
--headlessRuns without a simulator, inside the app’s JavaScript. Taps by position and gestures are reported as unsupported.
--ignore-flow-configIgnores the flow’s own config block.

In Studio, the same settings are in the Flows screen’s Replay options: Direct scroll, Auto-scroll, Fast settle, In-process delivery, Disable animations and Capture network.

Settings a flow declares

Some settings decide whether a flow tests the right thing. A flow that tests infinite scroll needs real drags; one that checks a screen’s own animation timing needs the fixed waits. Declare them in the flow:

YAML
flow: product list pagingconfig:  scrollMode: gesture       # gesture or direct  fastSettle: false         # use the fixed waits  autoScroll: true          # scroll an off-screen control into view  strictAssert: true        # a step that couldn't run fails the run  stepDelayFloorMs: 150     # pause after each step, ms (0–2000, default 80)  transitionSettleMs: 900   # longest wait before each step, ms (0–10000, default 600)  chaos: slow               # bad-network profile: slow, flaky, truncated, offline-midstepsteps: …

A misspelt key, or a value out of range, stops the flow from loading and lists the valid ones.

When a flag and the flow disagree

For one flow, a flag you type wins: a one-off run is what flags are for. For a folder of flows, the flow wins, because one flag can’t suit every flow in a suite. Try both:

RunFlag
offscreen-refusal.yaml
flow: offscreen refusalconfig:  autoScroll: falsesteps: …
Terminal
squiggle flow offscreen-refusal

Auto-scroll is off

Nobody said otherwise, and the flow and the default agree. Nothing to report.

Either way, the run prints which value it used before the first step:

Terminal
squiggle flows .squiggle/flows --direct-scroll! flow config: scrollMode=gesture (was direct) — declared by the flow (a batch run never overrides it)

A step’s own mode: beats both. Only flags you type count: a default never overrides the flow.

Run a flow on a bad network

--chaos degrades every request the app makes during the run, so you reach the loading, error and retry states without breaking your own connection:

ProfileWhat the app gets
slowEvery response, 2 seconds late.
flakyA 500 for the 1st, 6th, 11th… request. The same requests fail on every run.
truncatedEvery response body cut off halfway.
offline-midstepEverything works until the middle step (the earlier of the two, for an even number of steps), then every request fails as if the device went offline.
Terminal
squiggle flow orders --chaos offline-midstep

A flow whose subject is an error state can declare its profile instead: config: { chaos: flaky }. Then --chaos none runs it on a working network.

The run turns on network capture, which happens when the app launches. If the app is already running without it, the run fails before its first step and says so; run it again with --fresh. Requests to Metro and to Squiggle itself are never degraded.

Run a flow without the network

--stubs answers every request the app makes from an earlier run of the flow, so it runs with no server and no test data to maintain. First run it once against the network with --capture-net, then pass that run’s id, the name of its log in .squiggle/runs, or latest for the run just before:

Terminal
squiggle flow orders --capture-netsquiggle flow orders --stubs latestsquiggle flow orders --stubs mve7k1uh-2-4zs6

Each endpoint gets the status it returned most often, its median response time, and a body of the same shape: the same fields, types and list lengths, with placeholder values (title 1, title 2, ids counting up from 1). The run lists every stub before its first step.

A request that never got a response is not stubbed. An endpoint whose shape wasn’t recorded, because it isn’t JSON or the run had network capture off, is answered with its status and an empty body. Stubs combine with --chaos: a stubbed request is answered, and the profile degrades the rest.

Check every screen in each setup

--matrix runs the flow three times, each from a fresh launch: once in the setup the simulator is in, once in the other appearance (light or dark), and once at the largest text size. On the first visit to each screen it checks for controls that overlap, get cut off or end up off-screen, and reports only what a setup adds to the first run, since a problem in every setup is about the screen, not the setup. The simulator’s settings are put back afterwards, even when a run fails or you stop it with Ctrl-C.

Terminal
squiggle flow settings --matrix

The run lists each setup as as found (…), light or dark, and largest text, with each problem it adds and its source line. It exits 1 when a setup adds any. With --json it prints { cells: [{ cell, ran, screens, findings, added }] }, where each entry in added has a route, check, handle and detail.

--matrix needs an iOS simulator. On a Mac app it stops with E_MATRIX_PLATFORM and exit code 2.

Repair what a setup breaks

When a screen’s spacing is declared with defineTunable, --repair searches it for values that work in every setup. Name the tunables it may move:

Terminal
squiggle flow layout-lab --matrix --repair layout.gap,layout.padding,layout.minHeight  ✗ largest text: 1 screen(s) audited · 1 finding(s) · 1 not in the first cell    layout-lab · noClipping · 'layout-sign-out' is cut off by the viewport edge — 67% of it is on screen  ✗ before · as found (dark, large): loss 16.7: layout-edit-profile hitTarget 3.3 pt; …  ✗ before · light: loss 16.7: layout-edit-profile hitTarget 3.3 pt; …  ✗ before · largest text: loss 33.2: layout-sign-out noClipping 33.2 pt  ✔ search · grid: layout.gap 0, layout.padding 0, layout.minHeight 44: no finding in any cell after 23 probe(s)  ✔ after · as found (dark, large): loss 0  ✔ after · light: loss 0  ✔ after · largest text: loss 0  ✔ repaired: layout.gap 2, layout.padding 2, layout.minHeight 44: no finding in any of 3 cells, 32 probe(s)  .squiggle/tune/mvglnkg2-repair-8t5h.json · propose_fix { tuning: 'mvglnkg2-repair-8t5h' } turns it into an edit

After the matrix, it runs the flow once to reach its last screen and measures that screen in each setup, relaunching the app for each one. Each problem is scored by how far it is from fixed, in points: how far a control reaches past the screen’s edge, how deep two controls overlap, and how far a control falls short of 44 pt. The score is 0 only when nothing is wrong, and a control that was on the screen at the start must still be on it. It sets the tunables in the running app and changes nothing else.

It accepts values only when the score is 0 in every setup, including the one the simulator was in, so a fix for the largest text can’t break the default. Then it moves each value onto the nearest of the tunable’s tokens that still works, and flags a value it couldn’t move. It stops after 40 measurements for each setup that had a problem, and exits 1 when nothing it tried clears every setup. A control a scroll view can bring back doesn’t count as lost. --repair-search picks the search: grid (the default) tries the values in order from the smallest; random:<seed> and descent are there to check it against. --repair-control <seeds> runs that check: it counts what the grid, random search over that many seeds and descent each spend to reach a clean layout, on one set of measurements. On the Layout Lab the grid needed 13 measurements, random search 40 on average over 100 seeds, and descent never got there. The control takes thousands of measurements, so it keeps them in .squiggle/tune/ and a stopped run picks up where it left off. The result is saved under .squiggle/tune/, and an agent turns it into edits of each tunable’s default with propose_fix { tuning }.

Run a flow the way VoiceOver reaches it

--ax-only finds the control for every tap, type, swipe and scroll through the accessibility tree alone. A step that passes normally and fails here is a control a screen-reader user can’t reach.

Terminal
squiggle flow checkout --ax-only
  • Only testID, label, text and role can be matched, and text matches only the accessibility label, which is what VoiceOver reads. A step whose selector uses component, callSite, source, within, nth, point or route is reported as unsupported, and fails the run only with --strict-assert.
  • Two elements that VoiceOver announces the same way fail the step instead of one being picked.
  • There is no healing and no navigation fallback: a selector either reaches the control or the step fails.
  • Assertions still read the app’s own UI tree, so only how controls are reached changes.
  • It needs a simulator; with --headless every action is unsupported.

A step that fails here fails with ax_unreachable. Give the control an accessibilityLabel or a testID that names it uniquely.

Name where a tap lands

A tap that opens another screen can name it with expectRoute. Once that screen appears, the next step starts without waiting out the rest of the transition. Naming the wrong screen only costs the wait. Recorded flows fill it in for you.

YAML
- tap: { text: Checkout, expectRoute: OrderConfirmation }

Every key is in the Flow syntax reference.