Today I published version 2.3.5, a week after 2.3.0. It is not a big release: no new tools (still 55) and no speed numbers. It is a fix release, and most of the fixes come down to one idea: when Swipium's CI gate is green, something was actually tested.

It is live on GitHub and on npm as swipium@2.3.5. If you are new to the project, the 2.3.0 post covers the last big round of changes, and the docs cover setup.

A green gate means a real test

Before: a few paths could still get a run through the gate without a real test. A run whose only content was an agent's failure note could pass with a warnOn policy. With warnOn: ["visual_diff"], a failing assertVisible was softened to a warning, so a broken login check could slip through. And swipium report --latest could gate on yesterday's report when today's run saved none.

Now: an agent's note on its own never counts as testing, so that run is marked not tested and blocks. visual_diff only softens visual checks. --latest refuses an older report when a newer session saved nothing. And every export format, JUnit, SARIF, Playwright and Markdown, agrees with the gate: the headline never says SHIP while the gate blocks.

Flake detection works now too. Trend and flake history used to look for a file that is never written, so a flow was never marked flaky.

Why: a gate that is sometimes green for untested runs is worse than no gate at all.

Safe on repos you just cloned

Before: a cloned repo could make Swipium delete files outside the project. Evidence cleanup followed symlinks inside .swipium/, and how long evidence was kept came from a policy file the repo itself can commit.

Now: Swipium never follows a symlink anywhere under .swipium/, for writes, appends or deletes, and evidence is always kept for at least one day. Generated test files are only created inside the real project root. Test credentials (SWIPIUM_TEST_*) no longer reach builds, Metro, adb or other child processes, so they are much less likely to end up in a build log. And suite replay asks for consent before it runs seed scripts or toggles the network, so scripts from the repo never run unasked.

Why: pointing an agent at a repo you just cloned should not put the rest of your machine at risk. The threat model covers what Swipium trusts and what it does not.

Fewer surprises on real apps

Before: accented, CJK and emoji text could turn into replacement characters, breaking text selectors and read-backs. On iOS, an alert shown over a sheet was layered wrong, so its buttons were marked not tappable. A debug variant like com.acme.app.debug in front counted as com.acme.app. And a missing Android emulator binary crashed the whole MCP server.

Now: text arrives intact, iOS elements are in the right order, foreground checks match the exact package, and a missing emulator fails fast with a clear EMULATOR_BOOT_FAILED error. A background job that crashes is marked failed instead of looking like it is still running.

Also fixed

Assertions mean what they say. assertVisible no longer matches words from the app's package name. In com.acme.checkout, "Checkout" used to be visible on every screen.

Fewer stuck runs. WebDriverAgent never types text twice after a timeout, and cancelling now stops flows and iOS preparation.

A friendlier CLI. Usage errors explain the problem with a "did you mean" hint, --help exits 0, and swipium gc --days= with an empty value is rejected. It used to mean zero days and delete almost everything.

Verifiable releases. Packages are now published from CI with npm provenance, so you can check that what you install was built from the repository. The test suite grew from about 1,420 to about 1,650 tests.

Upgrading from 2.3.0

Heads up: the stricter gate can turn a CI run that was green into red. That is intended. Those runs were either not testing anything or hiding a real failure.

Restart your MCP client after upgrading. If your CI uses swipium report --latest --fail-on-gate, it now exits 2 when a newer session saved no report: add --max-age <minutes> or pass --session <id>. Policies with warnOn: ["visual_diff"] block on failing assertVisible steps again. Hand-written flows with invalid option values are now rejected, so run qa_flow_check on yours. And qa_bundletool refuses physical phones and asks you to pick when several devices are online.

To update and check your setup:

npx -y swipium@2.3.5 verify

The full upgrade checklist is in the changelog.

Thoughts

This release is mostly about security, plus a few things I had pending to fix and patch. There are a lot of fronts at the same time, and all of them change often. This version should make the MCP more robust to work with.

I'm using this tool for some personal projects, and so far it has helped me a lot by flagging blind spots and use cases I hadn't considered, beyond the usual development bugs.