← Plugin catalog
Developer Tools

Autonom

Andrei Iatsuk v0.30.0

Test and debug mobile apps

Full description ↓

View on ChatGPT ↗

Save this plugin to Following and return to review observed changes. Follow up to 10 plugins free.

Description

From the marketplace listing

Autonom gives AI coding agents a portable skill pack and JSON CLI to test and debug mobile apps on Android and the iOS Simulator: explicit target sessions, compact accessibility/UI trees, semantic taps and gestures, screenshots, recordings, logs, crash reports, deep links, permissions, app-container files, a consent-gated localhost MITM proxy for inspecting, mocking, and exporting HTTP(S) traffic, and Flow v1 — repeatable flow files with exact selectors, polling assertions, and machine-readable failure classes. Requires a separately installed Autonom CLI, Python 3.11+, and the device tools for your platform. The iOS Simulator requires macOS and Xcode.

Language: English · Automatically detected from descriptions.

What changed

Autonom

Oct 27, 2026 · 1 saved observations

Metadata · Oct 27, 2026 · 00:52 UTC · Compared with Oct 24, 2026 · 13:54 UTC

WHAT CHANGED

Package or technical metadata updated

Package or technical metadata updated

Observed in package metadata. These changes alone do not establish a new customer-facing feature.

Open full comparison.

Is v2 template

Before

Oct 24, 2026 · 13:54 UTC

Not present

After

Oct 27, 2026 · 00:52 UTC

false

Metadata: full comparison →

Competitor profile

From the developer’s listing and package; capabilities are not independently tested. Package observed Nov 3, 2026.

Declared capabilities

  • Interactive
  • Read
  • Write

Declared limitations

Specific limitations have not been established. Missing information does not mean unrestricted capabilities.

Cost & access

Pricing and account requirements have not been established from the available package declarations. Unknown pricing does not mean free access.

Starter prompt

Use Autonom to test, debug, profile, or validate this application with evidence-backed findings.

View package source →

Keywords

Matches for “auto”

Exact text from the indicated source. A mention alone does not establish support for your task.

Plugin name

Autonom

Package name

autonom

Publisher keywords · listing

adb ai-agents android autonom codex debugging emulator flutter har harness idb ios logcat memory mitmproxy mobile-qa mocking network performance qa screenshot session simulator testing ui-automation ui-tree

Publisher full description

Autonom gives AI coding agents a portable skill pack and JSON CLI to test and debug mobile apps on Android and the iOS Simulator: explicit target sessions, compact accessibility/UI trees, semantic taps and gestures, screenshots, recordings, logs, crash reports, deep links, permissions, app-container files, a consent-gated localhost MITM proxy for inspecting, mocking, and exporting HTTP(S) traffic, and Flow v1 — repeatable flow files with exact selectors, polling assertions, and machine-readable failure classes. Requires a separately installed Autonom CLI, Python 3.11+, and the device tools for your platform. The iOS Simulator requires macOS and Xcode.

Files & skills

File archives

Plugin package71 files · 109 KBBrowse files →
Skill instructions
android-app-actions1.55 KB

View saved version →

---
name: android-app-actions
description: Design, implement, and validate Android deep links, verified App Links, launcher shortcuts, external intents, and Assistant-facing actions through one typed and sanitized routing model.
---

# Android App Actions

Use one routing model for every external entry into the app. Prefer a small
surface over exposing the full navigation graph.

## Steps

1. List one to three high-value destinations or verbs the user actually needs.
2. Map each to the thinnest surface that fits: App Link / deep link, static or
   dynamic shortcut, notification action, widget action, or Assistant capability.
3. Funnel all external entry points through a single typed destination or
   domain-command type.
4. Validate and sanitize host, path, and query parameters before navigation or
   business logic runs.
5. Exercise cold start, warm start, duplicate launch, back stack, bad input, and
   missing content on a real device.

## Smoke invocation

```bash
adb -s <serial> shell am start -W \
  -a android.intent.action.VIEW \
  -d "https://example.com/path?item=123" \
  <package>
```

For Flutter, keep URI parsing and destination mapping unit-testable in Dart;
manifest verification and system launch remain Android concerns.

## Hard rules

- Do not open the entire navigation tree to the outside world.
- Do not invent parallel routers for links, shortcuts, notifications, and widgets.
- Never trust inbound ids, hosts, paths, or query values.
- Compilation of XML or manifests is not proof — invoke the action on a target
  and confirm the destination.

Referenced files: 1 — file references are metadata, not package contents.

android-debugger-agent2.22 KB

View saved version →

---
name: android-debugger-agent
description: Build, install, launch, inspect, and debug Android apps on explicit adb targets using Gradle or Flutter builds, screenshots, robust UI Automator selectors, input events, and focused logcat evidence.
---

# Android Debugger Agent

Drive a native or hybrid Android app on one explicit adb target. Prefer
Autonom CLI when a session is active; fall back to the helpers below when you
need raw adb or offline XML analysis.

## 1. Pin a single device

```bash
adb devices -l
emulator -list-avds
```

If more than one target is connected, pass `-s <serial>` on every adb call.

## 2. Build and install

Discover the real Gradle task; do not invent module names.

```bash
./gradlew tasks --all
./gradlew :app:installDebug --console=plain
```

Flutter apps: use `flutter-debugger-agent` for the Dart/engine path and keep
this skill for the Android shell layer. Stop at the first actionable build error.

## 3. Launch and capture state

```bash
PACKAGE=<application-id>
ACTIVITY="$(adb -s <serial> shell cmd package resolve-activity --brief "$PACKAGE" | tr -d '\r')"
adb -s <serial> shell am start -W -n "$ACTIVITY"
adb -s <serial> exec-out screencap -p > /tmp/android-screen.png
adb -s <serial> exec-out uiautomator dump /dev/tty > /tmp/android-ui.xml
```

## 4. Selectors and taps

```bash
python3 <skill-root>/scripts/ui_tree_summarize.py /tmp/android-ui.xml
python3 <skill-root>/scripts/ui_query.py /tmp/android-ui.xml --text Settings --json
python3 <skill-root>/scripts/ui_query.py --serial <serial> \
  --desc "Open settings" --wait 10 --click
```

Supported selectors: text, content-desc / Flutter semantics, resource-id, class,
package, booleans, exact / contains / regex, index, live wait. Ambiguous
matches must be narrowed or indexed — never silently tap the first hit.

## 5. Logs

```bash
adb -s <serial> logcat -c
PID="$(adb -s <serial> shell pidof -s "$PACKAGE" | tr -d '\r')"
adb -s <serial> logcat --pid "$PID"
adb -s <serial> logcat -b crash -d
```

## 6. Replay

After a fix, reuse the same target, setup, inputs, screenshot, query, and logs.

## Testing note

Compose test tags and Flutter semantics belong in automated tests. UI Automator
is for system UI and black-box evidence, not a substitute for widget tests.

Referenced files: 4 — file references are metadata, not package contents.

android-emulator-browser2.29 KB

View saved version →

---
name: android-emulator-browser
description: Mirror and lightly control an Android target or iOS Simulator in the visible Codex browser through the authenticated Autonom Mobile Canvas.
---

# Mobile Canvas browser

Stream one explicit target into a localhost browser for visual proof and light
control. Install and launch the app first. Canvas actions use the same Autonom
action pipeline and journal as CLI input; the browser is not a second runner.

## Launch

```bash
autonom canvas serve --platform android --serial <adb-serial> --transport auto

autonom canvas serve --platform ios --target <simulator-udid> --transport screencap
```

Open the printed fragment-token URL in the agent browser panel. The page
exchanges it for an HttpOnly cookie and removes the fragment. Leave the process
running and confirm frames advance before calling the setup successful.

## Transport modes

| Mode | Behavior |
| --- | --- |
| `auto` | Prefer `screenrecord` + ffmpeg H.264→MJPEG; fall back if unavailable |
| `screenrecord` | Require accelerated path; fail loudly when prerequisites missing |
| `screencap` | Multipart JPEG screenshots; no ffmpeg |

Android also exposes authenticated Annex-B H.264 at `/stream.h264`. iOS uses
public `simctl` screenshots with idb input and always chooses the screenshot
fallback; pixel-to-point mapping is handled by the Canvas status channel.

The UI shows active transport and frame health. Use Reconnect after rotation or
when device-side recording stops.

## Input surface

Tap, drag, wheel-as-swipe, Back/Home/Enter/Delete/D-pad/wake, and conservative
ASCII typing. Every action records `human`, `agent`, `replay`, or `system`
origin. The control endpoint supports pause, resume, takeover, and release.
Structural selection still belongs to Autonom `ui` commands.

## Security

Binds to `127.0.0.1` with a random token by default. Do not expose publicly.
Anyone with a forwarded port and the bootstrap token can drive the device.
`--no-auth` is for isolated local debugging only.

## Performance boundary

Good for iteration; not a measurement tool. Use scrcpy for high-fidelity manual
review and Macrobenchmark / Perfetto / Flutter profile mode for claims.

## Evidence to record

Side-panel screenshot, platform and target id, transport, package/activity,
variant, control owner, and the exact flow replayed.

Referenced files: 3 — file references are metadata, not package contents.

android-memory-leaks2.11 KB

View saved version →

---
name: android-memory-leaks
description: Investigate Android Java, Kotlin, native, and process memory using repeatable flows, meminfo, heap dumps, retaining paths, heapprofd, and before-after evidence without confusing growth with proof.
---

# Android Memory Leaks

Growth is a lead. A leak is a retained object with a root path, or a repeatable
accumulation under an equivalent flow.

## Evidence ladder

1. Directional snapshots via `dumpsys meminfo` and `/proc/<pid>/status`.
2. Managed-heap dump when the process is debuggable and the suspect lives in
   Java/Kotlin heap.
3. Class counts, retained size, fields, and GC roots (Studio Profiler,
   LeakCanary, Shark).
4. Perfetto `heapprofd` for native allocation growth.
5. Same flow again after the fix; same metrics and paths.

## Capture

Prefer the Autonom CLI — artifacts land in the session's `metrics/` dir and
the journal keeps the timeline:

```bash
autonom metrics snapshot --label baseline        # cheap directional point
autonom metrics memory capture --label after-flow [--no-hprof]
autonom metrics memory analyze                   # series math, leads only
autonom metrics series --count 5 --interval 2    # snapshots + slope/leads
```

Pass `--no-hprof` for release/profileable builds where dumpheap is
unavailable. The standalone scripts remain for hosts without a session:

```bash
<skill-root>/scripts/capture_android_memory.sh \
  --serial <serial> --package <application-id> \
  --out-dir <artifact-dir> --label after-flow
python3 <skill-root>/scripts/analyze_meminfo_series.py <artifact-dir> \
  --glob '*-meminfo.txt' --json
```

## Interpretation rules

- One spike ≠ leak.
- Prove managed leaks with a retaining path or repeatable accumulation.
- Separate Dart/Flutter heap from Android managed and native memory.
- Separate intentional process caches from feature-lifetime objects.
- Fix the retaining edge; do not sprinkle forced GC or blanket clear calls.

## Frequent retainers

Activities, fragments, platform views, contexts, listeners, coroutines,
adapters, bitmaps, singletons, plugin registrars, event channels, JNI handles,
and caches without clear ownership.

Referenced files: 3 — file references are metadata, not package contents.

android-project-setup1.87 KB

View saved version →

---
name: android-project-setup
description: Create, inspect, or modernize native Android modules with Kotlin, Gradle, Compose, SDK configuration, version catalogs, and compatibility checks based on the active repository and official guidance.
---

# Android Project Setup

Change the smallest coherent surface that the repository already uses.

## Workflow

1. Read settings, module builds, version catalogs, wrapper, `gradle.properties`,
   manifests, build logic, and CI.
2. Run `scripts/toolchain_snapshot.py` instead of trusting a hardcoded matrix.
3. Classify: new module, focused dependency change, or toolchain migration.
4. Keep existing dependency-management and architecture conventions when they
   are coherent.
5. Re-check official AGP / Kotlin / Gradle / library docs for version-sensitive
   work.
6. Apply a minimal patch and run the narrowest Gradle tasks that prove it.

## Defaults

- Checked-in Gradle wrapper only.
- Versions live in the existing catalog or shared build logic.
- Stable production deps unless a required API needs a preview artifact.
- Prefer KSP when supported; do not mix processor migration with unrelated work.
- `minSdk` follows product policy; `compileSdk` / `targetSdk` changes need
  platform review.
- AGP built-in Kotlin only after verifying the active AGP migration guide.

## Verify

```bash
./gradlew help --console=plain
./gradlew :app:assembleDebug --console=plain
./gradlew :app:testDebugUnitTest --console=plain
./gradlew :app:lintDebug --console=plain
```

Discover tasks with `./gradlew tasks --all`.

## Guardrails

- No dynamic dependency versions.
- No generic template pasted over a live project.
- No broad dependency upgrades during a feature-only task.
- No package rename + architecture rewrite + toolchain bump in one change
  unless the user asked for all of it.
- No “latest” claims without an observed snapshot and a dated official source.

Referenced files: 1 — file references are metadata, not package contents.

android-runtime-performance2.27 KB

View saved version →

---
name: android-runtime-performance
description: Capture and interpret Android runtime performance with Macrobenchmark, Baseline Profiles, Perfetto, Simpleperf, gfxinfo, and comparable device flows while separating native and Flutter evidence.
---

# Android Runtime Performance

Measure one user-visible flow on an explicit device. Separate Flutter UI/raster
evidence from Android/native evidence.

## Choose the instrument

- **Macrobenchmark** — repeatable startup / interaction metrics on a
  profileable or release-like build.
- **Baseline Profiles** — hot paths only; confirm the shipped binary consumes
  the profile.
- **Perfetto** — frame lanes, scheduler, binder, locks, I/O, GC, custom sections.
- **Simpleperf** — sampled CPU stacks (not wall-clock waits by itself).
- **gfxinfo** — quick directional frame snapshot.
- **heapprofd** — native allocation stacks when managed-heap evidence is not enough.

Read `references/native-performance-workflows.md` before capture.

## Fast frame snapshot

Prefer the Autonom CLI inside a session — it owns reset/capture, keeps the
raw text as the artifact, and journals the run:

```bash
autonom metrics frames reset
# replay one focused flow
autonom metrics frames capture
autonom metrics trace --preset simpleperf --duration 30
```

Raw adb equivalent without a session:

```bash
adb -s "$SERIAL" shell dumpsys gfxinfo "$PACKAGE" reset
# replay one focused flow
adb -s "$SERIAL" shell dumpsys gfxinfo "$PACKAGE" framestats \
  > "$ARTIFACT_DIR/gfxinfo-framestats.txt"
```

## Simpleperf

```bash
adb -s "$SERIAL" shell rm -f /data/local/tmp/perf.data
adb -s "$SERIAL" shell simpleperf record \
  --app "$PACKAGE" -e cpu-clock -f 4000 -g \
  --duration 30 -o /data/local/tmp/perf.data
adb -s "$SERIAL" pull /data/local/tmp/perf.data "$ARTIFACT_DIR/perf.data"
<skill-root>/scripts/simpleperf_report.sh \
  "$ARTIFACT_DIR/perf.data" "$ARTIFACT_DIR" \
  --first-party-regex 'com\\.example|libapp\\.so'
```

## Heapprofd reports

```bash
<skill-root>/scripts/heapprofd_report.sh "$ARTIFACT_DIR/trace.pftrace" "$ARTIFACT_DIR"
```

## Comparison contract

Record device, Android version, refresh rate, package/variant, Flutter mode,
compilation mode, data state, thermal state, exact flow, and run count. Compare
only equivalent captures; state units and aggregation.

Referenced files: 4 — file references are metadata, not package contents.

autonom9.2 KB

View saved version →

---
name: autonom
description: Start here for Autonom — the universal mobile test/debug harness for AI agents on Android and the iOS Simulator. The map of the whole system — what it can do, how it works, and how to use it end to end (own a session, see the screen, drive it, watch logs and network, mock responses, capture evidence, journal every step). Read this first when asked to test, debug, profile, reproduce, or validate a running app, or when unsure which Autonom skill to load. Then let project-router narrow to the stack-specific skills.
---

# Autonom — how the whole harness works

Autonom lets an AI agent **test and debug a real mobile app** on an Android
emulator or an iOS Simulator, with evidence. You describe a goal in plain words;
this skill is the map that tells you what the harness can do and how to drive it.
The same verbs work on both platforms.

## When this applies

Any task about a *running* app: reproduce a bug, walk a flow, check a screen,
read logs, see what the app sent and how it reacts to a bad response, profile,
or validate a release. If the task is only about *writing* code, route straight
to the stack skills via `project-router`.

## How it is built (the mental model)

Three layers, and it helps to know which is which:

1. **The CLI control plane** — `scripts/autonom.py` (installed as `autonom`).
   One dependency-free JSON API. Every command prints JSON; an expected failure
   prints `{"ok": false, "error_code": "...", "hint": "..."}` on stderr with exit
   code 2, so you branch on a stable code, never parse prose. This is the source
   of truth; the skills are how to use it well.
2. **Thin verb-skills** — `mobile-session`, `mobile-screen`, `mobile-network`
   wrap the CLI verbs with the judgement to use them.
3. **Knowledge** — `mobile-memory` (`~/.autonom/apps/<pkg>/`) carries what was
   learned about an app once, so a flow is replayed, not re-derived.

Sessions, mocks, the process registry, and per-app knowledge are all
**machine-global** under `~/.autonom/` — a run is not tied to the directory it
was launched from, and the active session is found from anywhere.

## First moves

```bash
autonom doctor          # what can this machine actually do? tools, session, orphans
autonom devices         # Android + iOS in one list; each entry has a `running` flag
```

`doctor` is honest: green means installed, not proven. If something is missing it
names the exact fix.

## The end-to-end loop

```bash
# 1. Own a target (boot it first if needed)
autonom devices boot --avd Pixel_9                 # or --udid <sim>; session start also boots a sim
autonom session start --serial emulator-5554 --app-id com.example.app

# 2. See the screen, then act by meaning (not coordinates)
autonom ui tree                                    # compact accessibility tree
autonom ui tap --desc "Log In"
autonom ui type "test-user"                        # into the focused field

# 3. Watch what happens
autonom session outputs                            # what is followable + tail -f hints
autonom logs follow --source device --grep 'Error' --max-seconds 60   # live, bounded NDJSON
autonom network start --i-understand-mitm          # decrypt + record (consent-gated)
autonom network attach --i-understand-mitm
autonom network requests list --host api.example.com --status 401
autonom metrics snapshot --label baseline          # memory/CPU point, honest semantics

# 4. Force a failure and check the UI reacts
autonom network mock add --url '.../v1/login' --status 500 --json '{"error":"x"}'
autonom ui tap --desc "Log In"
autonom ui find --text "Something went wrong"

# 5. Capture evidence + record what you did
autonom screenshot --task login --label "500 error state"
autonom note add "login shows the generic error banner on 500; retry works" --task login

# 6. Read the run back, then clean up
autonom journal                                    # the full timeline of this session
autonom session stop                               # tears down proxy/stream best-effort
```

Everything from step 1–6 is appended to the session **journal** automatically
(`~/.autonom/sessions/<id>/journal.ndjson`): every verb, its scrubbed arguments,
the result, and your notes. `autonom journal --kind note` / `--verb 'ui tap'`
reads it back — the record you use to re-check or hand off a flow.

## What you can do (verb catalog)

| Area | Verbs |
| --- | --- |
| Discover | `doctor`, `devices`, `devices boot/shutdown` |
| Session | `session start/show/stop/launch/force-stop/clear/uninstall` |
| Screen | `ui tree/find/wait/tap/swipe/type/key` (`ui wait --settled`, `ui tree --format outline`); `find`/`tap` resolve on-screen matches first, and a node that is not on screen is `visible: false` in the tree and refused by `tap` (`element_offscreen`); `pinch/rotate/shake` are refused on both platforms; iOS input via idb or AXe, named in `backend` |
| First run | `tour` — overview, the workflow, this machine's targets, and `--run` to walk three screens into Settings: before/after screenshots, hierarchy, and device log attached to each tap or back, an assertion that the screen changed after each, an HTML/JUnit report, and a written account |
| Evidence | `screenshot` (with `width`/`height`), `shots list/show`, `record start/stop`, `note add/list`, `journal` |
| Deterministic state | `simulator status-bar pin\|clear` (both platforms), `simulator keyboard pin\|reset\|show` (iOS), `simulator animations pin\|reset\|show` (Android) — remove clock, battery, animation, and autocorrect noise from before/after comparisons; `verified` only after a read-back |
| Device state | `open` (deep link; Android names the activity that took it in `handled_by`), `permissions`, `location` (iOS + Android emulator), `media add`, `file ls/pull` |
| Diagnostics | `logs tail`, `crash list/show` |
| Live observation | `session outputs`, `logs follow`, `network requests follow`, `journal --follow` — bounded NDJSON streams |
| Metrics | `metrics snapshot/series/list-presets`, `metrics memory capture/analyze/warn`, `metrics frames …`, `metrics trace --preset …` |
| Network | `network start/stop/status/attach/detach`, `network requests list/show`, `network mock …`, `network export --har` |
| Flows | `flow check/fmt/list/run` — repeatable Flow v1 files with exact selectors and failure classes |
| Housekeeping | `processes`, `cleanup` |

## Which skill for what

- **`project-router`** — load next: classifies the repo and narrows to the
  stack-specific skills. This skill is the map; the router is the dispatcher.
- **`toolchain-doctor`** — read `autonom doctor`, inspect SDK/toolchain state.
- **`mobile-session`** — own a target, the artifact dirs, the journal.
- **`mobile-screen`** — trees, semantic find/tap, gestures, screenshots.
- **`mobile-network`** — capture, mock, HAR — consent-gated MITM.
- **`mobile-flow`** — repeatable Flow v1 files: author, validate, run, replay.
- **`mobile-memory`** — read/write per-app knowledge and flow runbooks.
- **`android-debugger-agent` / `ios-debugger-agent` / `flutter-debugger-agent`**
  — build-run-inspect for a specific stack.
- Domain packs — `flutter-*`, native `android-*` / `compose-*` — for testing,
  performance, memory, release validation, platform layers.
- **`autonom-setup`** — install or hand the harness off to another machine/agent.

## The evidence ladder

Climb it; do not skip:

```
code → unit/widget test → integration on an explicit target →
screenshot + UI tree + logs → profile / memory / network → before/after replay
```

## Rules that keep the work honest

- **Consent is real.** `network start/attach` decrypt traffic and change device
  config: they need their `--i-understand-mitm` (and `--install-ca`) flag every
  time, plus a typed phrase on a terminal. Consent is never cached.
- **Exit 0 ≠ it worked.** A tap returning ok does not prove the screen changed.
  Compare before/after trees or screenshots.
- **A mocked screenshot is a lie waiting to happen.** Captures taken while a mock
  is active are flagged `screenshot_shows_mocked_data`; never present them as real
  backend behaviour.
- **Never surface secrets** — tokens, keystores, `.env`. The journal and captures
  mask credential-shaped values by default; keep it that way.
- **One explicit target.** With more than one device attached, pass `--target` /
  `--serial` / `--udid`; ambiguity is an error listing candidates, never a guess.
- **iOS text lives in `desc`** (from `AXLabel`), not `text` — select with `--desc`.

## Where things live

```
~/.autonom/sessions/<id>/   shots, trees (full history), logs, network, recordings,
                            crashes, files, journal.ndjson, session.json,
                            flows/<run_id>/events.ndjson  (flow run evidence)
~/.autonom/apps/<package>/  per-app knowledge: app.md, flows/, bodies/  (mobile-memory)
~/.local/state/autonom/     mocks registry, process registry  (machine-level, persistent)
```
Flow *files* are repository source (conventionally `.autonom/flows/` in the
app repo — see `mobile-flow`); only run artifacts land under `~/.autonom`.

## Related

- `project-router` — the next skill to load; narrows to the stack.
- `mobile-session`, `mobile-screen`, `mobile-network`, `mobile-memory` — the verbs.
- `docs/USAGE.md`, `docs/CAPABILITIES.md`, `AGENTS.md` — deeper reference.

Referenced files: 1 — file references are metadata, not package contents.

autonom-setup3.96 KB

View saved version →

---
name: autonom-setup
description: Install or hand off the Autonom harness — the CLI plus all skills — onto a new machine or into another agent. Build a single transferable bundle, copy it, install with one command, verify, and register it with Claude, Grok, or Codex. Triggers when setting up Autonom somewhere new, packaging it for handoff, or asking how to get it onto another device or agent.
---

# Autonom Setup

Autonom is a plugin: 23 skills plus a dependency-free CLI they drive. This skill
packages the whole thing into one file you can carry to another machine and
install with one command — the skills travel with the CLI and land in whatever
agent is there.

Not a compiled binary, on purpose: the CLI is stdlib-only Python (nothing to
compile), and the network addon must be a real file on disk because `mitmdump`
loads it by path. So the transferable artifact is a **tarball** — one file to
copy, one command to install.

## Build the bundle

```bash
./scripts/build_release.sh
# -> dist/autonom-<version>.tgz  (~200 KB: CLI + library + all skills + manifest)
```

The bundle carries its own `install.sh`, so the target needs nothing but
`python3` (already present on macOS and Linux) and, for real device work, the
device tools (`adb`, `xcrun`, `idb`, `mitmdump`) that `doctor` checks.

## Install on another machine

Copy `dist/autonom-<version>.tgz` over any transport, then:

```bash
tar xzf autonom-<version>.tgz
./autonom-<version>/install.sh          # checkbox picker: tools, Claude, Codex, Grok
./autonom-<version>/install.sh --all    # headless: everything
./autonom-<version>/install.sh claude   # or any explicit subset
```

`install.sh` copies the bundle to a stable home (`AUTONOM_PREFIX`, default
`~/.local/share/autonom`) so the extracted folder can be deleted afterwards,
puts `autonom` on `PATH`, installs the selected device tools and agents, and
verifies with `autonom doctor`. Headless runs never prompt — no flags means
CLI only. Overrides: `AUTONOM_PREFIX`, `AUTONOM_BIN_DIR`.

## Into each agent

`install.sh <target>`, or run the layer directly:

```bash
./scripts/install_claude.sh              # Claude Code plugin (marketplace + install)
./scripts/install_codex.sh               # Codex plugin (marketplace + add)
./scripts/install_skills.sh grok         # ~/.grok/skills
./scripts/install_skills.sh --link <dir> # any skill-root, by symlink
./scripts/install_skills.sh --copy <dir> # by copy (no live repo)
```

Claude and Codex install Autonom as a **plugin** from the bundle's marketplace
manifests and copy it into a versioned cache. After the source changes, re-run
the script — or `claude plugin update autonom@autonom` /
`codex plugin add autonom@autonom` — so the cache catches up; the version is
bumped on every release exactly so those caches notice. Skills load namespaced
(`autonom:mobile-network`). Without the `claude` CLI the Claude path falls back
to loose `autonom-*` skills in `~/.claude/skills`.

## Verify it actually works

`doctor` reports what is present; a green install still means *installed*, not
*proven*. Confirm the chain end to end on a connected device:

```bash
autonom doctor                     # tools + capabilities + session + orphans
autonom devices                    # a real device/simulator is listed
autonom session start --platform android --target <id>
autonom ui tree --max-nodes 5      # a real tree comes back
autonom session stop
```

If any step fails, `doctor`'s per-tool `state` and install hint name what is
missing — do not proceed as if it worked.

## Knowledge stays behind

The bundle ships the **skills**, not the per-app knowledge under
`~/.autonom/apps/` (see `mobile-memory`). That store is machine-local and
per-person; it accumulates on each machine as work happens and is not part of
the handoff.

## Related

- `mobile-session`, `mobile-screen`, `mobile-network` — the verbs the CLI exposes.
- `mobile-memory` — per-app knowledge, kept out of the bundle on purpose.
- `toolchain-doctor` — reading `autonom doctor` output.

Referenced files: 1 — file references are metadata, not package contents.

compose-performance-audit1.62 KB

View saved version →

---
name: compose-performance-audit
description: Audit Compose performance from code and runtime evidence, focusing on invalidation scope, stability, lazy identity, effects, layout, draw work, images, and comparable profiling captures.
---

# Compose Performance Audit

Find unnecessary recomposition, layout, and draw work with code review plus
comparable runtime captures.

## Order of work

1. Name one screen and one user-visible symptom (jank, slow open, scroll hitch).
2. Trace state owners: which state reads invalidate which composables.
3. Check stability of parameters, `key` / identity in lazy lists, and effect
   scopes (`LaunchedEffect`, `DisposableEffect`, `SideEffect`).
4. Look for expensive work in composition, intrinsic measurements, excessive
   modifiers (clip / graphicsLayer / shadow), oversized images, and layout
   thrash.
5. Capture a comparable profile (Macrobenchmark, Perfetto, or Studio) on an
   explicit target and mode.
6. Apply the smallest fix; re-run the same flow.

## Code signals

- Unstable lambdas / objects crossing composition boundaries without remember.
- Reading broad state higher than needed.
- Lazy items without stable keys.
- `derivedStateOf` misused or missing where it would shrink invalidation.
- Layout that measures children twice without need.
- Images decoded at full resolution for small on-screen size.

## Runtime signals

Frame time, recomposition counts (when available), overdraw, and timeline
sections. Emulator numbers are directional; use a physical device for claims
users will feel.

## Report

Flow, device, mode, code findings, capture artifacts, fix, and before/after
metrics with units.

Referenced files: 1 — file references are metadata, not package contents.

flutter-android-platform1.62 KB

View saved version →

---
name: flutter-android-platform
description: Implement and debug the Android platform layer of Flutter apps including Gradle, manifests, flavors, permissions, platform channels, plugins, app links, notifications, and native lifecycle behavior.
---

# Flutter Android Platform

Own the Android shell of a Flutter app: Gradle, manifests, flavors, plugins,
channels, links, notifications, and lifecycle.

## Inspect first

- `android/` Gradle files, flavors, application ids, signing config location
- manifests and merged manifest output
- plugin registrations and GeneratedPluginRegistrant assumptions
- platform channels and method/event APIs
- deep links / App Links, notification intents, launch modes

Use `toolchain-doctor` / `toolchain_snapshot.py` for SDK and AGP pins.

## Common work

1. Keep Flutter Gradle plugin and Android embedding conventions the project
   already uses.
2. Add permissions and queries only with a product reason; document why.
3. Implement channel handlers with explicit error codes and main-thread rules.
4. Verify deep links with `adb` VIEW intents and App Link verification where
   required.
5. Confirm flavor × build-mode matrix before release packaging.

## Debug loop

```bash
flutter build apk --debug --flavor <flavor>
adb -s <serial> install -r <apk>
adb -s <serial> shell am start -W -n <package>/<activity>
adb -s <serial> logcat --pid "$(adb -s <serial> shell pidof -s <package> | tr -d '\r')"
```

## Boundaries

- Do not “fix” Flutter UI bugs by rewriting Android unless evidence points
  there.
- Do not print keystore secrets or signing material.
- Prefer repository scripts for flavors and dart-defines.

Referenced files: 1 — file references are metadata, not package contents.

flutter-debugger-agent1.41 KB

View saved version →

---
name: flutter-debugger-agent
description: Build, launch, attach to, inspect, and debug Flutter applications on explicit devices using Flutter machine output, Dart VM service evidence, adb, screenshots, logs, and repeatable user flows.
---

# Flutter Debugger Agent

Debug a Flutter app on one explicit device with machine-readable Flutter output
plus Android evidence when needed.

## Resolve the tool chain

```bash
python3 <marketplace-root>/scripts/flutter_exec.py --root . -- --version
python3 <marketplace-root>/scripts/flutter_exec.py --root . -- devices
```

Never silently fall back to a system Flutter when the repo is FVM-pinned.

## Run / attach

```bash
python3 <marketplace-root>/scripts/flutter_exec.py --root . -- \
  run -d <device-id> --target <target> --machine
```

Capture VM service URI from machine events when you need DevTools or custom
inspectors. Prefer Autonom session + `ui` / `screenshot` / `logs` once the app
is up.

## Evidence ladder

1. Reproduce on a single pinned device/mode.
2. Screenshot + accessibility / UI tree (Autonom or adb).
3. Dart logs / VM service errors.
4. `adb logcat` filtered to the app process for platform issues.
5. After a patch, replay the same flow end-to-end.

## Rules

- One device id on every command when multiple are present.
- Debug mode is fine for functional bugs; profile mode for performance claims.
- Do not treat a green build as a green UX path — observe the screen.

Referenced files: 1 — file references are metadata, not package contents.

flutter-memory-leaks1.37 KB

View saved version →

---
name: flutter-memory-leaks
description: Investigate Flutter and Android memory growth using Dart retaining paths, DevTools snapshots, disposal and lifecycle review, native heap evidence, and comparable before-after flows.
---

# Flutter Memory Leaks

Separate Dart heap, external/graphics memory, Android managed heap, and native
allocations. Compare equivalent idle points.

## Ladder

1. Define one flow and an equivalent idle baseline.
2. Capture DevTools heap snapshots (or allocation tracing) before and after.
3. Inspect retaining paths for growing types.
4. Review disposal: controllers, streams, timers, listeners, `BuildContext`
   capture, image caches, statics, platform-channel subscriptions.
5. Escalate to platform metrics when process growth is not explained by Dart
   snapshots: `autonom metrics snapshot` / `series` on both platforms, then
   `android-memory-leaks` (`autonom metrics memory capture|analyze`) for the
   Android evidence pack.
6. Fix the retaining edge; repeat the same flow.

Read `references/memory-evidence.md`.

## Rules

- RSS/PSS growth alone is not proof.
- Disposed ≠ unreachable; check the path.
- Do not paper over leaks with forced GC or global cache wipes.
- Add leak-tracking libraries only if the repo already uses them or the user
  asks.

## Report

Flow, device, mode, artifacts, retained types, path or accumulation proof,
fix, before/after.

Referenced files: 2 — file references are metadata, not package contents.

flutter-performance-audit1.6 KB

View saved version →

---
name: flutter-performance-audit
description: Audit Flutter runtime performance from code and profile evidence, separating UI-thread build cost, raster cost, startup, memory pressure, image work, and native Android bottlenecks.
---

# Flutter Performance Audit

Separate UI-thread build cost from raster cost. Measure in **profile mode** on
an explicit device.

## Evidence order

1. One user-visible flow and symptom.
2. Code pass: rebuild scope, sync Dart work, list identity, images, layout/paint,
   state churn, isolate boundaries.
3. Profile-mode capture on a named device.
4. DevTools frames / timeline / CPU / raster / custom `dart:developer` events.
5. For repeatable flows: `integration_test` `watchPerformance` or `traceAction`;
   keep the JSON.
6. Smallest fix; equivalent re-capture.

Read `references/performance-evidence.md` first.

## Commands

```bash
flutter run --profile -d <device-id> --target <target>
flutter test integration_test/performance_test.dart -d <device-id>
autonom metrics frames flutter-summary build/integration_response_data.json
```

(`<skill-root>/scripts/frame_timings_summary.py` is the same math for hosts
without the Autonom CLI.)

## Metrics

Report build and raster average, p90, p99, worst, and over-budget frames. State
the budget (refresh rate). Keep startup and app size as separate claims.

## Boundaries

- Debug timings are not representative.
- Emulator results are directional.
- Flutter UI/raster evidence does not replace Perfetto/Simpleperf for Android,
  binder, codec, DB, or native plugin bottlenecks.
- Compare same device, thermal state, mode, target, data, and flow.

Referenced files: 3 — file references are metadata, not package contents.

flutter-release-validation1.51 KB

View saved version →

---
name: flutter-release-validation
description: Validate Flutter Android release candidates across flavors, targets, Dart defines, signing boundaries, analysis, tests, APK or app bundle builds, smoke flows, and app-size artifacts.
---

# Flutter Release Validation

Lock the exact flavor, target, application id, version, dart-defines, signing
source, backend environment, and output artifact before building.

## SDK selection

```bash
python3 <marketplace-root>/scripts/flutter_exec.py --root . -- --version
```

Respect FVM pins; no silent system fallback.

## Ladder

1. Clean only if stale build state is plausible.
2. Resolve deps / codegen via repo commands.
3. Format, analyze, release-critical tests.
4. Build the requested artifact.
5. Install the closest installable variant on an explicit device.
6. Smoke: startup, auth/bootstrap, navigation, network failure, deep links,
   background/foreground, one critical transaction.
7. Record artifact paths, sizes, mapping/symbols, checksums when required.

## Typical commands

```bash
flutter analyze
flutter test
flutter build appbundle --release --flavor <flavor> --target <target> \
  --analyze-size
```

Use APK for local smoke when the deliverable is an AAB. Prefer project scripts
for `--dart-define-from-file`.

## Signing

Never print keystore material or service-account JSON. Report only which signing
source was used and whether verification succeeded.

## Report

Commit, toolchain snapshot, flavor/target/mode, commands, tests, artifacts,
device smoke, blockers, untested areas.

Referenced files: 1 — file references are metadata, not package contents.

flutter-testing1.62 KB

View saved version →

---
name: flutter-testing
description: Design and run a layered Flutter test strategy across unit, widget, golden, integration, platform-channel, and native-UI tests with focused commands and reproducible device setup.
---

# Flutter Testing

Pick the narrowest layer that proves the behavior.

| Layer | Owns |
| --- | --- |
| unit | pure mapping, validation, state, repositories |
| widget | UI states, semantics, forms, callbacks without a full device |
| golden | visual contracts with pinned fonts/locale/size/renderer |
| integration_test | full Flutter flows on a real target |
| native | Kotlin/plugin implementation |
| Patrol / Espresso / UI Automator | permissions, notifications, platform views |

```bash
python3 <marketplace-root>/scripts/flutter_exec.py --root . -- --version
```

Read `references/testing-boundaries.md` when Flutter and native UI meet.

## Loop

1. Read the change, acceptance criteria, nearby tests, past flakes.
2. Draft happy / boundary / failure / lifecycle / a11y / platform cases.
3. Implement highest-signal missing tests first.
4. Run, refine once from failures, then widen the suite.

## Commands

```bash
flutter test test/path/to/test.dart
flutter test --plain-name "specific behavior"
flutter test integration_test/app_test.dart -d <device-id>
```

Use `flutter drive` only when the project’s driver/lab requires it.

## Stability

Prefer type/text/semantics/key finders. Avoid unbounded `pumpAndSettle` when
animations or streams never idle. Control clocks, randomness, network, and
storage.

## Platform channels

Fake the Dart side, unit-test native handlers, and keep one device path for
each critical channel.

Referenced files: 2 — file references are metadata, not package contents.

ios-debugger-agent7.68 KB

View saved version →

---
name: ios-debugger-agent
description: Build, run, and debug iOS apps on the Simulator for AI agents — resolve the scheme and destination, install and launch, then climb an evidence ladder through accessibility trees, screenshots, logs, and crash reports.
---

# iOS Debugger Agent (Simulator)

## Purpose

Turn "the iOS app misbehaves" into measured evidence. Build with the project's own
toolchain, drive the app through the Autonom CLI, and separate what was observed
from what is inferred.

Physical devices are out of scope: they need Developer Mode, signing, and tunnels.

## Prerequisites

```bash
python3 <autonom-root>/scripts/autonom.py doctor
```

Everything except `ui *` works with Xcode alone. The accessibility tree
additionally needs `idb` plus `idb_companion`; input (tap, swipe, type, key)
needs a companion that can load SimulatorKit, or AXe. `doctor` reports
`capabilities.ios_tree` and `capabilities.ios_hid` separately and prints the
exact fix for each gap.

**Xcode 27 trap:** SimulatorKit moved to `Contents/SharedFrameworks`, and an
`idb_companion` built before that looks in `Library/PrivateFrameworks`. The
tree still works, but every input fails with `ios_hid_framework_missing`.
Fix it with `brew update && brew upgrade idb-companion`, or install AXe
(`brew install cameroncooke/axe/axe`): the default `--ios-hid auto` then
routes input through AXe while the tree stays on idb, and each `ui` input
payload reports `backend: axe`. Pin a route with `--ios-hid idb|axe` (or
`AUTONOM_IOS_HID`) before the verb. Do not report a failed tap as an app
defect until `doctor` shows input ready. On Xcode 27 with AXe carrying input,
`capabilities.ios_hid.ready` stays `false` (it describes idb's own HID) while
`ios_hid.axe_ready` is `true`, `ios_hid.backend` is `axe`, and
`capabilities.ios_ui.ready` is `true`: read `ios_ui` and `axe_ready`, and keep
going — input works.

## 1. Build

Use whatever the repository already uses — never introduce a second build path.

```bash
# Flutter
flutter build ios --simulator --debug
# -> build/ios/iphonesimulator/Runner.app

# Xcode project or workspace
xcodebuild -scheme <Scheme> -destination 'platform=iOS Simulator,name=iPhone 17 Pro' \
  -derivedDataPath build/DerivedData build
# -> build/DerivedData/Build/Products/Debug-iphonesimulator/<App>.app

# SwiftPM executable targets
swift build
```

Resolve the scheme from `xcodebuild -list` rather than guessing. When FVM or a
pinned Flutter SDK is present, resolve it with `scripts/flutter_exec.py` instead of
falling back to a system SDK.

## 2. Own a session

```bash
python3 <autonom-root>/scripts/autonom.py devices --platform ios
python3 <autonom-root>/scripts/autonom.py session start \
  --platform ios --target <UDID> \
  --install <path>.app --launch --app-id <bundle-id> --log-stream
```

`--log-stream` starts a background `log stream` for the session, filtered to
the app and capped on disk (`AUTONOM_IOS_LOG_MAX_MB`, default 50, one
rotation), which makes `logs tail` cheap afterwards. Without it, tailing
falls back to `log show`. A second `session start` while one is live is
refused with `session_already_active`; stop the old one first.

## 3. Reproduce and capture

```bash
python3 <autonom-root>/scripts/autonom.py ui tree
python3 <autonom-root>/scripts/autonom.py ui find --desc "Log In" --mode exact
python3 <autonom-root>/scripts/autonom.py screenshot --out /tmp/before.png
python3 <autonom-root>/scripts/autonom.py ui tap --desc "Log In" --mode exact
python3 <autonom-root>/scripts/autonom.py screenshot --out /tmp/after.png
python3 <autonom-root>/scripts/autonom.py logs tail --package <bundle-id> --since 60
python3 <autonom-root>/scripts/autonom.py crash list --app-id <bundle-id>
```

**Compare the two screenshots.** A tap that exits 0 has not been proven to do
anything; a changed screen (or a changed tree) is the proof. Pin the status
bar first (`simulator status-bar pin`) so the battery and signal glyphs do not
show up as a difference, and pin the keyboard before typed text must be exact:

```bash
python3 <autonom-root>/scripts/autonom.py simulator status-bar pin
python3 <autonom-root>/scripts/autonom.py simulator keyboard pin --value locale=en-US --value reboot=true
```

The keyboard pin writes the simulator's preference store (autocorrect,
prediction, auto-capitalisation off; locale set) and needs the device shut
down — `reboot=true` cycles it for you. Undo with `keyboard reset` and
`status-bar clear`.

## 4. Drive state the UI cannot reach

```bash
python3 <autonom-root>/scripts/autonom.py open "myapp://order/42"
python3 <autonom-root>/scripts/autonom.py permissions grant photos <bundle-id>
python3 <autonom-root>/scripts/autonom.py permissions reset all <bundle-id>
python3 <autonom-root>/scripts/autonom.py location set 55.751244,37.618423
python3 <autonom-root>/scripts/autonom.py media add fixtures/photo.png
python3 <autonom-root>/scripts/autonom.py file ls Documents --app-id <bundle-id>
python3 <autonom-root>/scripts/autonom.py file pull Documents/state.json --app-id <bundle-id>
python3 <autonom-root>/scripts/autonom.py session launch <bundle-id> --setenv FLAVOR=staging
python3 <autonom-root>/scripts/autonom.py simulator biometric enroll     # then match | nonmatch | unenroll
```

`simulator biometric match|nonmatch` posts the Face ID notification on a
Face ID device and the Touch ID one otherwise (both when the device type
does not say; `biometry` reports which), through the Simulator's Darwin
notifications. Nothing reports whether a prompt consumed it, so it answers
`verified: false` with the reason in `verification`; prove the effect from
the app's screen. Unknown
actions are `invalid_simulator_action` with the valid list.

File access is confined to the app container; a path that escapes it is refused.
Pulled files are written into session artifacts and their contents are not echoed,
because app data can contain personal information.

## 5. Evidence ladder

1. code inspection;
2. narrow unit / XCTest;
3. explicit-simulator integration flow;
4. screenshot + accessibility tree + logs + crash reports;
5. recording of the exact repro (`record start` / `record stop`);
6. equivalent before/after replay after the fix.

Report measured facts, code-backed findings, hypotheses, and remaining uncertainty
as four separate categories.

## Boundaries and honesty rules

- The tree is the **accessibility** hierarchy. A Flutter view without `Semantics`
  can be nearly empty; that is reported as `sparse_accessibility_tree` and means
  the app exposes little, not that the screen is blank.
- Clear app state with `session clear` (uninstall + reinstall). `--strategy privacy`
  resets permissions **only** and leaves data in place.
- Load questions have CLI verbs: `autonom metrics snapshot` (host `ps` view of
  the Simulator process + data-container size — never comparable to Android
  PSS), `metrics series` for direction, `metrics memory warn` as a best-effort
  pressure stimulus, and `metrics trace --preset allocations|time-profiler|
  leaks|hitches` for real Instruments `.trace` bundles. `hitches` is
  physical-device only: Instruments refuses Animation Hitches on the
  Simulator (`list-presets` says `unsupported_on_simulator`), so on the
  Simulator use `time-profiler` for main-thread stalls or
  `metrics frames flutter-summary` on Flutter frame timings. Do not present a
  screenshot or a log line as a performance measurement.
- Simulator behavior differs from a physical device for camera, sensors, push,
  background execution, and memory pressure. Say so when it matters to the finding.

## Related

- `mobile-session`, `mobile-screen`
- `mobile-network` — inspect and mock the app's HTTP(S) traffic
- `ios-project-setup` — scheme, simulator, and bundle-id conventions
- `flutter-debugger-agent` — Dart-side debugging for Flutter apps

Referenced files: 1 — file references are metadata, not package contents.

ios-project-setup3.69 KB

View saved version →

---
name: ios-project-setup
description: Inspect an iOS project's real layout for AI agents — resolve workspace vs project, schemes, configurations, bundle identifiers, simulator destinations, and the built .app path without hard-coding versions.
---

# iOS Project Setup

## Purpose

Establish **what this repository actually builds** before running anything. Every
value below is discovered from the project, never assumed. Do not assert a "latest"
or "recommended" Xcode, Swift, or deployment-target version; report what is
declared.

## 1. Toolchain, as installed

```bash
xcode-select -p
xcodebuild -version
xcrun simctl list runtimes
python3 <autonom-root>/scripts/autonom.py doctor
```

## 2. Workspace or project

```bash
ls *.xcworkspace *.xcodeproj Package.swift 2>/dev/null
```

| Found | Build with |
| --- | --- |
| `*.xcworkspace` | `-workspace <name>.xcworkspace` — required when CocoaPods is used |
| `*.xcodeproj` only | `-project <name>.xcodeproj` |
| `Package.swift` | `swift build` / `swift test` |
| `ios/` inside a Flutter app | drive Flutter; touch Xcode only for native/platform work |

A Flutter repository has `pubspec.yaml` with a Flutter SDK dependency and an `ios/`
directory containing `Runner.xcworkspace`. Route Dart work to the `flutter-*`
skills; use this skill for signing, capabilities, plist, or native module work.

## 3. Schemes, configurations, destinations

```bash
xcodebuild -list -workspace <name>.xcworkspace
xcodebuild -showBuildSettings -scheme <Scheme> | grep -E 'PRODUCT_BUNDLE_IDENTIFIER|PRODUCT_NAME|IPHONEOS_DEPLOYMENT_TARGET|CONFIGURATION_BUILD_DIR'
xcrun simctl list devices available
```

Take the bundle id from `PRODUCT_BUNDLE_IDENTIFIER` rather than reading `Info.plist`
directly — the plist usually contains `$(PRODUCT_BUNDLE_IDENTIFIER)`, and per-flavor
schemes can differ.

Destination for a simulator build:

```text
-destination 'platform=iOS Simulator,id=<UDID>'      # exact, preferred
-destination 'platform=iOS Simulator,name=iPhone 17 Pro'
```

Prefer the UDID from `autonom devices --platform ios`: a name can match several
runtimes.

## 4. Locate the built `.app`

```bash
# Flutter
build/ios/iphonesimulator/Runner.app

# xcodebuild with an explicit derived-data path
build/DerivedData/Build/Products/<Configuration>-iphonesimulator/<PRODUCT_NAME>.app
```

Pass that path to `session start --install`. Storing it in the session is what lets
`session clear` do a full uninstall + reinstall later.

## 5. Flavors and configurations

Multi-flavor apps usually carry one scheme per flavor and per-configuration
`PRODUCT_BUNDLE_IDENTIFIER` values. Confirm which scheme maps to which bundle id
with `-showBuildSettings` before installing, so the session's `--app-id` matches
what was actually built.

## Accessibility readiness

`ui find` selects by label and identifier. Before a UI-automation pass, check the
app exposes them:

- SwiftUI — `.accessibilityLabel("Log In")`, `.accessibilityIdentifier("login_button")`
- UIKit — `accessibilityLabel`, `accessibilityIdentifier`
- Flutter — `Semantics(label: ..., identifier: ...)`

An identifier is worth more than a label: it survives localization, so a selector
built on it does not break when the app is translated.

## Rules

- Never hard-code an Xcode, Swift, or iOS version as "current".
- Never modify signing settings, certificates, provisioning profiles, or
  entitlements as a side effect of a build or test task.
- Keep secrets out of logs: no keychain contents, API keys, or `.env` values.
- Prefer the repository's existing build entry point over inventing a new one.

## Related

- `ios-debugger-agent` — build, run, and gather evidence
- `mobile-session`, `mobile-screen`
- `project-router` — decide whether iOS skills are needed at all

Referenced files: 1 — file references are metadata, not package contents.

mobile-flow8.83 KB

View saved version →

---
name: mobile-flow
description: Author, validate, and run repeatable Flow v1 files for AI agents — strict YAML flows with exact selectors, polling assertions, single-fire mutations, failure classes, and per-run event evidence via the Autonom CLI.
---

# Mobile Flow (Android + iOS Simulator)

## Purpose

Turn a working journey into a **repeatable, reviewable flow file** — and run
it with exact semantics. A flow is strict YAML in the app repository
(conventionally `.autonom/flows/`, shared steps in `.autonom/subflows/`);
runtime artifacts land under `~/.autonom/sessions/<id>/flows/<run_id>/`.

The language refuses what it cannot execute exactly: unknown commands,
fuzzy-by-default matching, YAML cleverness (anchors, block scalars, tabs),
and implicit retries of mutating actions. Every rejection carries
`file:line:column` and a stable `error_code`. Full reference: `docs/FLOW.md`.

## CLI

```bash
# Validate (whole runFlow graph, no device needed)
python3 <autonom-root>/scripts/autonom.py flow check .autonom/flows
python3 <autonom-root>/scripts/autonom.py flow check login.yaml

# Canonical form — expands shorthand, materializes `match: exact`
python3 <autonom-root>/scripts/autonom.py flow fmt login.yaml --write
python3 <autonom-root>/scripts/autonom.py flow fmt .autonom/flows --check   # exit 1 = needs formatting
python3 <autonom-root>/scripts/autonom.py flow fmt login.yaml --write --drop-comments   # only when losing comments is intended

# Enumerate: file, id, name, tags, platforms
python3 <autonom-root>/scripts/autonom.py flow list

# Run (needs an active session — see mobile-session)
python3 <autonom-root>/scripts/autonom.py flow run login.yaml
python3 <autonom-root>/scripts/autonom.py flow run .autonom/flows --include-tag smoke --exclude-tag flaky
python3 <autonom-root>/scripts/autonom.py flow run login.yaml --secret TEST_PASSWORD --env LOCALE=en_US
python3 <autonom-root>/scripts/autonom.py flow run login.yaml --events     # NDJSON stream on stdout
python3 <autonom-root>/scripts/autonom.py flow run login.yaml --dry-run    # pre-flight only; top-level status "planned", `planned` list with runtime indexes

# Approve for promotion: N consecutive clean replays of the current bytes
python3 <autonom-root>/scripts/autonom.py teach approve login.yaml --run --minimum-runs 3 --secret TEST_PASSWORD --env LOCALE=en_US
```

`flow check` catches what used to fail only on the device: negative
`timeoutMs`/`delayMs`/`durationMs`/`maxSwipes`/`chars`, a latitude or
longitude out of range, an empty selector string, and a `match: regex`
pattern that does not compile (`timeoutMs: 0` is legal: one check). An
unknown command, argument, selector field, or match mode names its closest
legal spelling (`Did you mean 'tapOn'?`). `flow fmt --write` refuses with
`comments_would_be_lost` rather than silently dropping YAML comments; in a
directory nothing is written until every file is safe.

## A minimal flow

```yaml
schema: autonom.dev/flow/v1
appId: com.example.app
name: Login
tags: [smoke, auth]
---
- launchApp
- tapOn: Sign in
- inputText:
    value: ${TEST_EMAIL}
    sensitive: true
- assertVisible:
    selector:
      id: home_screen
    timeoutMs: 7000
```

`flow fmt` expands `- tapOn: Sign in` into the explicit selector form with
`match: exact`. Selectors: `id`, `text`, `description`, `role`, plus
`enabled`/`checked`/`selected` and `index`. **iOS puts the visible label in
`description`, not `text`** — cross-platform flows prefer `id`, then
`description`.

## Reading a run

- Exit `0`: passed. Exit `1`: a **test failure** — the app did not do what
  the flow asserts; the stdout summary carries `status: "failed"` and
  `failure.failure_class: "test_failure"`. Exit `2`: the flow or the
  machine is wrong (definition/infrastructure) — stderr envelope as usual.
- Assertions poll (default 10 s, per-step `timeoutMs`); mutating steps fire
  **exactly once** — a duplicate selector match refuses instead of tapping.
- Selectors match **on-screen** nodes only, and duplicates and `index` are
  counted among them: iOS lists Flutter's scroll cache at a 0x0 frame, and a
  flow never asserts, taps, or counts such a node (`scrollUntilVisible` keeps
  scrolling until it is really on screen). Whitespace in a label is
  normalised, so one selector matches the label iOS joins with a newline and
  Android with a space. Before a tap on a sheet that is still animating in,
  add `waitForSettled` — it waits for a still tree and never fails the flow.
- A directory suite runs on one target and skips a flow that cannot run there
  (declared for the other platform, or an Android-only step on iOS) with a
  `flow_skipped_for_platform` warning; `flow list` shows each flow's
  inferred `platforms` up front.
- Per-step events: `~/.autonom/sessions/<id>/flows/<run_id>/events.ndjson`;
  a failing step leaves a screenshot and a hierarchy dump automatically.
- `onFlowComplete` cleanup always runs; its failures are reported separately
  (`hook_failures`) and never mask the primary outcome.
- A test failure also carries `repair`: the `--until-step` command that
  reconstructs the state the failed step assumed, `ui tree`, the old selector
  as a widened `ui find … --mode contains --all`, and the re-verification
  commands, with advice keyed by the error code. It names the step that
  ended the run (never a recovered retry or a cleanup hook), the file that
  holds it (`flow`; `root_flow` is what gets replayed), and `--secret NAME` /
  `--env NAME=<value>` placeholders — never values. When the failing step's
  hierarchy was captured, `candidates` ranks up to five on-screen nodes by
  similarity to the failed selector, each with a ready Flow `selector` and
  the `ui find` command that confirms it. Run them in order, confirm a
  candidate, edit the YAML, and re-run — the brief never rewrites the flow
  for you.
- A run that aborted on a definition or infrastructure error is a JUnit
  `<error>` (`errors="1"`), never a green suite.

## Rules

1. Never put credentials in a flow file. Pass `--secret NAME`; reference it
   as `${NAME}`. Values never enter the file, events, journal, or summary.
   An `--env` value that reaches a `sensitive: true` slot is treated as a
   secret too (redacted, reproduced as `--secret NAME`, and flagged with a
   `flow_env_value_sensitive` warning) — switch it to `--secret`.
2. Prefer `id`, then unique visible text (`description` on iOS). Use
   `index` only for a justified duplicate, never to paper over a bad
   selector.
3. Do not fight a `test_failure` by retrying the flow — read the failure
   evidence first; the app state, not the harness, is what changed.
   `launchApp` starts the app fresh (cleared task; `resume: true` to
   continue where it was), and `inputText` polls for a focused field before
   typing (`timeoutMs`), so the two first-step failures seen on real devices
   — a resumed subscreen and typing before the field existed — no longer
   need hand-written waits.
4. Keep subflows atomic (login, dismiss-permissions) and let `runFlow`
   compose them; recursion and paths escaping the workspace are refused.
5. Approvals are bound to content. `teach approve` counts only replays whose
   recorded `flow_sha256` (and every `runFlow` child's `subflow_sha256`)
   matches the current bytes; the receipt stores both, and `app-skill
   promote` refuses a flow edited since. Edit, then replay again — an
   mtime-preserving copy does not count.
6. Flow files are source — commit them. If the repository blanket-ignores
   `.autonom/`, un-ignore the flows subtree (`!.autonom/flows/**`).

## Failure codes

| error_code | Meaning |
| --- | --- |
| `flow_parse_error` | YAML-subset violation; `file`/`line`/`column`/`reason` extras |
| `flow_unknown_command` / `flow_command_invalid` | not a v1 command / bad arguments |
| `flow_selector_invalid` | unknown or deferred selector field, bad match mode |
| `flow_file_not_found` / `flow_path_escapes_workspace` / `flow_cycle_detected` | runFlow graph problems |
| `flow_assertion_timeout` | test failure: the asserted state never held |
| `flow_no_focused_field` | test failure: `inputText` found nothing with keyboard focus to type into (`requireFocus: false` opts out) |
| `flow_var_undefined` / `flow_secret_undefined` | `${VAR}` unresolved / `--secret` not in the environment |
| `flow_no_flows_found` | no flow files (or none match the tag filters) |
| `flow_source_changed` | `teach approve` / `app-skill promote`: the flow or a `runFlow` child changed since its replays or receipt |
| `comments_would_be_lost` | `flow fmt --write` would drop comments; pass `--drop-comments` to accept that |
| `no_active_session` | run `session start` first (see mobile-session) |

## Related

- `mobile-session` — owning the device session a flow runs in.
- `mobile-screen` — exploring the UI to find stable selectors first.
- `mobile-memory` — prose runbooks; convert a stable runbook into a flow.
- `docs/FLOW.md` — the machine-checked language reference.

Referenced files: 1 — file references are metadata, not package contents.

mobile-memory3.91 KB

View saved version →

---
name: mobile-memory
description: Reuse and record per-app Autonom knowledge for device work. Read the app's overlay and flow runbooks before driving a device, and save a new flow only when it is durable, reusable, and evidence-backed. Triggers on a device task against a known app (a package/bundle id is in play), and after working out a repeatable procedure — make an order, mock a screen, log in, reproduce a bug.
---

# Mobile Memory

Per-app knowledge that a device task needs but the CLI cannot know: the backend,
how the app's network is wired, where its response schemas live, and step-by-step
flows worked out once and replayed after. It exists so the same hour of digging
is not spent twice.

## Where it lives

```
~/.autonom/apps/<package>/
  app.md              identity, backend, network model, TLS, schema roots, gotchas
  flows/<name>.md     a runbook: goal, endpoint, schema source, exact steps, verify
  bodies/<name>.json  ready mock payloads that flows reference
```

**Machine-local and per-person**, plain markdown — not in the app repo, not in
the Autonom repo. The same idea as a personal memory store: it accumulates as you
work and stays yours. The skill (this file) ships with Autonom; the knowledge
does not.

## Before a device task

1. Resolve the package id — from `--app-id`, the current session
   (`autonom session show`), or the repository under test.
2. Read `~/.autonom/apps/<package>/app.md` if it exists. It answers, up front,
   the questions that otherwise surface as wasted time: which backend, does the
   app cache the proxy, is `dart:io` even used, where are the schemas.
3. If the task matches a flow (mock a screen, make an order), read
   `flows/<name>.md` and follow it instead of re-deriving the endpoint, schema
   and gotchas.
4. Nothing there yet? Proceed normally — and consider leaving a flow behind when
   you crack something that will recur.

## When to save — and when not

Save **only** knowledge that is durable, reusable, and evidence-backed: the exact
endpoint, the exact glob, the path to the schema, the gotcha that cost time.
Prefer updating the existing file over adding a near-duplicate.

Do **not** save one-off exploration, obvious single commands, or anything already
in the code. **Never** write secrets — reference "log in as the test driver", not
an account or token. Keep each entry id-centric and concrete (`com.example.rides`,
`POST /api/v1/orders`, `lib/models/**/*_response.dart`), never vague.

A flow is worth a file when it is multi-step and will happen again. A single verb
is not a flow.

## Format

- **app.md** — identity (package, backend, build), how the network works (native
  vs `dart:io`, proxy caching), TLS/CA notes, where schemas live, a *known
  screens* table (screen → endpoint → flow), and gotchas already handled by the
  tool so a reader does not redo them.
- **flows/<name>.md** — Goal · Endpoint · Schema source · Steps (exact CLI) ·
  Verify · Gotchas. Reconstruct a response schema from the freezed model:
  `@JsonKey(name: ...)` is the JSON field, `required` without `?` is mandatory.
  These are prose runbooks — working knowledge. When one has become fully
  deterministic (stable selectors, no judgment calls), propose converting it
  into an Autonom Flow v1 file in the app repository (`mobile-flow`), which
  the CLI can then validate and replay exactly.
- **bodies/<name>.json** — a captured or hand-built payload a flow points at, so
  the shape is not rebuilt each time.

## How it connects

`project-router` detects the app → this skill loads its overlay and any matching
flow → the `mobile-session` / `mobile-screen` / `mobile-network` skills run the
CLI verbs → new knowledge is written back here. The knowledge layer sits beside
the thin verb-skills; it does not replace them.

## Related

- `project-router` — routes here once a device task targets a known app.
- `mobile-session`, `mobile-screen`, `mobile-network` — the verbs a flow drives.

Referenced files: 1 — file references are metadata, not package contents.

mobile-network17.8 KB

View saved version →

---
name: mobile-network
description: Inspect and mock a mobile app's HTTP(S) traffic for AI agents — start a localhost MITM proxy, attach a device with explicit consent, filter recorded requests, force responses, and export HAR without leaking credentials.
---

# Mobile Network (inspect and mock)

## Purpose

Answer **what did the app actually send and receive**, and **what does it do when
the server misbehaves**. Backed by mitmproxy, bound to localhost, scoped to one
Autonom session.

Use it when the task mentions an API, a request, a response, a status code, a
mock, a HAR, a proxy, or "why is this call failing".

## Before you start: this is privileged

Starting the proxy decrypts and records traffic. Attaching a device changes that
device's network configuration, and `--install-ca` writes to its trust store.
None of that happens without **explicit consent for that exact action**:

- every privileged verb requires its flag (`--i-understand-mitm`, plus
  `--install-ca` for a trust-store write);
- on an interactive terminal you must additionally type a confirmation phrase;
- consent is **never** cached — a grant earlier in the session does not carry to
  the next command;
- there is no environment variable or config file that grants it.

Only use this against devices and apps you own or are authorized to test.

## Workflow

```bash
# 1. Own a session first
python3 <autonom-root>/scripts/autonom.py session start --serial emulator-5554 --app-id com.example.app

# 2. Start the proxy (127.0.0.1 only; port is auto-chosen when omitted)
python3 <autonom-root>/scripts/autonom.py network start --i-understand-mitm

# 3. Point the device at it
python3 <autonom-root>/scripts/autonom.py network attach --i-understand-mitm

# 4. Exercise the app, then look
python3 <autonom-root>/scripts/autonom.py network requests list --path '*/login'
python3 <autonom-root>/scripts/autonom.py network requests list --host api.example.com --status 401
python3 <autonom-root>/scripts/autonom.py network requests show f_0003

# 5. Force a failure and check the UI reacts
python3 <autonom-root>/scripts/autonom.py network mock add \
  --match '*/v1/login' --method POST --status 500 \
  --body-file fixtures/login_error.json --header 'Content-Type: application/json'
python3 <autonom-root>/scripts/autonom.py ui tap --text "Log In"
python3 <autonom-root>/scripts/autonom.py ui find --text "Something went wrong"

# 6. Keep the evidence, then clean up
python3 <autonom-root>/scripts/autonom.py network export --har network/session.har
python3 <autonom-root>/scripts/autonom.py network mock disable --all
python3 <autonom-root>/scripts/autonom.py network detach
python3 <autonom-root>/scripts/autonom.py network stop
```

Step 6 uses `disable --all`, not `clear`: the registry is persistent, so `clear`
throws away rules you may want tomorrow. Use `clear` only to actually delete them.

`session stop` performs detach and stop best-effort anyway, in that order, so the
device is never left pointing at a dead proxy.

## Transparent capture on Android (no app change)

The workflow above sets the device's **global HTTP proxy** and trusts the CA in
the **user** store. That is enough for a native app with a debug
`network_security_config`, but it captures **nothing** from a Flutter app:
`dart:io` ignores the Android global proxy, and apps targeting API 24+ do not
trust user CAs. The fix routes the whole emulator through the proxy at launch and
puts the CA in the **system** store — with **zero changes to the app**:

`-http-proxy` is a **launch-time** flag, so pick the proxy port up front and pass
the *same* port to the boot and to `network start`. The emulator tolerates the
proxy being down at boot and connects once `network start` brings it up on that
fixed port. This is the exact order verified live on a rooted API 37 emulator:

```bash
# 1. Boot a dedicated, rootable AVD ROUTED through a chosen port (here 18091).
#    `-http-proxy` cannot be injected into an already-running emulator.
python3 <autonom-root>/scripts/autonom.py devices boot --avd Autonom_Mitm_API37 --http-proxy 127.0.0.1:18091
# -> note the serial it reports (e.g. emulator-5554); use it as <serial> below

# 2. Own a session on that emulator
python3 <autonom-root>/scripts/autonom.py session start --serial <serial> --app-id com.example.app

# 3. Start the proxy on the SAME port the emulator was routed to
python3 <autonom-root>/scripts/autonom.py network start --port 18091 --i-understand-mitm

# 4. Install the MITM CA into the SYSTEM trust store (reversible; needs root)
python3 <autonom-root>/scripts/autonom.py network attach --system-ca --i-understand-mitm --serial <serial>

# 5. Launch/drive the app, then look — Flutter/Dio requests now decrypt
python3 <autonom-root>/scripts/autonom.py session launch com.example.app
python3 <autonom-root>/scripts/autonom.py network requests list
```

`network start` is session-bound, which is why the session is owned first (step
2) and the port is fixed by hand rather than auto-chosen. `network attach
--system-ca` verifies the install from a zygote mount namespace and reports
`system_ca.verified`; a failed verify is a `system_ca_unverified` warning, not a
hard failure (HTTP is still captured, and the change is reversible).

**Hard constraint — the image must be rootable.** Use a `google_apis` system
image (userdebug: `adb root` succeeds or adbd already runs as uid 0), **not** a
`google_apis_playstore` image and not a production build — those block root, and
`network attach --system-ca` then refuses with `unsupported_capability`
(`capability: network.system_ca`). Use a **dedicated test AVD**; the change is a
tmpfs plus a bind into the zygote mount namespaces, and a **reboot clears all of
it** — never the host, never a real device. **Verified live on API >= 34** (the
APEX conscrypt store, `apex_conscrypt`); the API < 34 path uses the
`/system/etc/security/cacerts` remount instead and is **not yet verified on a
device**.

If the emulator was not booted routed to this session's proxy, `network attach
--system-ca` refuses with `unsupported_capability`
(`capability: network.transparent_capture`) and tells you to reboot with
`devices boot --avd X --http-proxy <session proxy>` — you cannot inject
`-http-proxy` into a running emulator. It also refuses (`reason:
app_proxy_attached`) while an app-proxy `network attach` is active: run `network
detach` first, so the device's previous global proxy is restored.

In transparent mode every flow on the proxy is **counted as the device's**, so
`network status` answers `attached: true` with `evidence: "transparent_proxy"`,
`target_flow_count == recent_flow_count`, and `unattributed_flow_count: 0`. The
flows all arrive from `127.0.0.1` by design, and any host process can reach the
proxy on loopback too — a `curl` through its port would be counted as well. That
is a reasonable default on a **dedicated test host**, where nothing else uses
the port; keep it that way while you gather evidence. The count holds only while
the process registry still shows this emulator booted routed through the proxy:
after `devices shutdown`, or a reboot without `--http-proxy`, `network status`
answers `attached: false` with `evidence: "transparent_route_gone"` and a
`reason` (unless a guest-network `10.0.2.x` flow still proves attachment),
and loopback flows are unattributed again. `network requests` lists
**all** captured flows. `network status` also reports `capture_mode` and the
installed `system_ca` (`verified`, `checked_via`). A fresh `adb shell ls
/apex/com.android.conscrypt/cacerts` will **not** show the cert (a different
mount namespace); Autonom verifies from a zygote namespace with `nsenter`.

The emulator's `-http-proxy` opens every tunnel as `CONNECT <ip>:443`, so the
connection target is an IP. Each flow's `host` is still the **name** the app
asked for (the Host header), and the IP is kept in `server_ip`. So `network
requests list --host backend.example.com` and `network mock add --host
backend.example.com` work as usual in transparent mode.

## Attaching, per platform

| Platform | How | Coverage |
| --- | --- | --- |
| Android emulator (transparent) | `--system-ca` on an emulator booted with `devices boot --http-proxy`: routes at launch + system-store CA | full, incl. Flutter/`dart:io` (see the section above); needs a rootable `google_apis` image |
| Android emulator (app-proxy) | sets the device's global HTTP proxy to `10.0.2.2:<port>` + user-store CA (`--install-ca`) | native apps with a debug `network_security_config`; **not** Flutter/`dart:io`, **not** pinned traffic |
| Android physical | **refused** — the proxy is loopback-only and a physical device cannot reach it; widening the bind would expose an open proxy | none |
| iOS Simulator | injects proxy environment variables into apps launched by `session launch` | clients honouring proxy env vars |

**iOS limitation, state it in findings:** the per-process mechanism covers
clients that read the proxy environment — curl and many SDKs. Native
`URLSession` reads the *system* proxy configuration and is **not** captured this
way. The Simulator has no proxy pane of its own — it uses the host Mac's
network stack — so the manual steps `network attach` prints set the **host's**
system proxy (and switch it off again afterwards). Ask the operator before
doing that, then confirm with `network status`. On iOS `network attach`
answers `attached: "unknown"` with `mode: manual` and `attach_state: manual`:
nothing has been observed yet, and the attach is not automatable the way
the emulator's is.

**Flutter needs an in-app hook, on both platforms.** `dart:io`'s `HttpClient`
ignores the proxy environment *and* Android's global proxy setting unless the
app itself sets `findProxy`. Add a hook to the debug build — for example
`HttpOverrides.global` with a `createHttpClient` that sets
`client.findProxy = HttpClient.findProxyFromEnvironment` (iOS), or returns
`'PROXY 10.0.2.2:<port>'` (Android emulator) — then relaunch. iOS
`network attach` says so in a `flutter_proxy_hook_required` warning. Without
the hook a Flutter app's requests never reach the proxy, whatever
`network status` says about the device.

Autonom never changes macOS network-service settings itself: that is a
system-wide change whose blast radius is the operator's whole machine.

## HTTPS and certificates

Decrypting TLS needs the app to trust the MITM CA.

- **Android, no app change at all:** transparent capture (see "Transparent
  capture on Android" above) — `network attach --system-ca` on a rooted emulator
  booted with `devices boot --http-proxy`. This is the only path that captures a
  release-config Flutter app without rebuilding it.
- **Preferred when you can rebuild:** point a **debug build** at a
  `network_security_config` (Android) that trusts user CAs, or use a debug trust
  configuration on iOS.
- **iOS Simulator:** `--install-ca` runs `simctl keychain add-root-cert`, scoped to
  that one simulator.
- **Android emulator:** `--install-ca` (with `--i-understand-mitm`) runs
  `adb root` and copies the CA into the user trust store. It works only on a
  rootable `google_apis` image: a `google_apis_playstore` image blocks
  `adb root`, and the attach fails with `backend_failed` saying so. A
  physical device is refused. The same steps by hand (`/system` needs no
  remount):

  ```bash
  CA=~/.local/state/autonom/ca/mitmproxy-ca-cert.pem
  HASH=$(openssl x509 -inform PEM -subject_hash_old -in "$CA" | head -1)
  adb -s <serial> root
  adb -s <serial> push "$CA" /data/local/tmp/$HASH.0
  adb -s <serial> shell "cp /data/local/tmp/$HASH.0 /data/misc/user/0/cacerts-added/$HASH.0 \
    && chown system:system /data/misc/user/0/cacerts-added/$HASH.0 \
    && chmod 644 /data/misc/user/0/cacerts-added/$HASH.0"
  ```

  Even then the app must trust **user** CAs through its `network_security_config`.

**Certificate pinning defeats all of this by design.** If the app pins, requests
fail and there is nothing to inspect. Say so plainly and use a debug build with
pinning disabled. Do not attempt to bypass pinning in a production build.

## Reading the results

```json
{
  "id": "f_0003", "method": "POST", "url": "https://api.example.com/v1/login",
  "host": "api.example.com", "path": "/v1/login", "status": 401, "duration_ms": 42,
  "request_headers_preview": {"authorization": "<redacted>"},
  "request_body_preview": "{\"email\": \"a@b.c\", \"password\": \"<redacted>\"}",
  "mocked": false, "mock_id": null,
  "sizes": {"request_bytes": 120, "response_bytes": 80}
}
```

- **Credentials are masked before anything is written to disk** — sensitive headers
  and credential-shaped body fields (`password`, `token`, `api_key`, …). Do not
  work around this to "see the real value".
- **URLs are scrubbed too.** Values of sensitive query and fragment keys
  (`token`, `access_token`, `api_key`, `key`, `secret`, `password`, `auth`,
  `session`, `signature`, `code`, AWS signature keys, and similar — decoded
  and case-insensitive) and the password in `user:password@host` become
  `<redacted>` in the stored `url` and `Referer`, again in `requests
  list/show`, and in HAR export; other query parameters (`page=2`) stay.
- Bodies are **2 KiB previews** by default. `--capture-bodies` persists full bodies
  and is off deliberately: bodies are the densest source of secrets and personal
  data. `requests show --full` needs it and warns when used.
- Listing is capped (default 50) and reports `total_matched` and `truncated`, so a
  partial view is never mistaken for the whole story.

## Mock semantics

Rules live in a **persistent, machine-level registry**
(`~/.local/state/autonom/mocks/registry.json`), not in the session. They survive
proxy restarts, session restarts and reboots, and every session shares one set.

```bash
# One-liner: this endpoint returns this JSON.
autonom network mock add --url 'https://api.devbackend.net/post/update/12341' \
  --json '{"status":"ok"}' --note 'ticket-123 repro'

# Or a glob, when the id varies
autonom network mock add --match '*/post/update/*' --method POST --status 500

autonom network mock list [--all]        # --all includes disabled rules
autonom network mock show m_1            # rule + a scrubbed body preview
autonom network mock update m_1 --status 503
autonom network mock disable m_1 | --all # keeps the rule, stops it firing
autonom network mock enable  m_1 | --all
autonom network mock remove m_1          # deletes the rule and its body
autonom network mock clear               # deletes everything
```

- `--url` matches the endpoint **exactly** and ignores the query string, so
  `…/12341` also matches `…/12341?ts=9` but never `…/123415`. `--match` takes a
  glob and leaves query handling to you.
- Match on URL, plus optional method and host. **First enabled rule wins.**
- A matched request **never reaches the origin** — the response is manufactured,
  not rewritten after the fact.
- Rules reload without restarting the proxy, so they can be swapped mid-scenario;
  a corrupt registry keeps the last good set rather than dropping everything.
- Mock CRUD needs **no session and no device** — rules can be prepared in advance.
- Rules are validated when added or updated: a target (`--url` or `--match`)
  is required (`selector_required`), a body that looks like JSON must parse,
  `--status` must be 100..599, and each `--header` must be `Name: value`.
- The registry lives outside any repository on purpose: a mock body is often a
  captured response, and a captured response often carries a token.

### The stale-rule hazard

Persistence has a price: a rule enabled last week fires again the moment a proxy
starts, and a fabricated response looks exactly like a real one. The defence is
that everything says so — `network start`, `network status` and `doctor` all
report `mocks.active` and raise `persistent_mocks_active`.

**Before trusting any network evidence, check `mocks.active` is what you expect.**
If a response looks wrong or suspiciously convenient, run `network mock list`
before concluding anything about the backend. `requests list --mocked true` shows
exactly which flows were faked.

## Honest reporting

1. Report status codes and bodies as **measured facts**, on-screen text separately.
2. `network status` reports `attached` as `true`, `false`, or **`unknown`**,
   with the `evidence` behind it, and `attach_state` (`automated`, `manual`,
   `not_attached`) for how far the attach got. Only traffic *from the target*
   counts: a flow from the emulator's guest network (`target_flows`), else the
   device's proxy setting read back (`device_setting`); a host `curl` through
   the proxy never does. Loopback flows are unattributed
   (`unattributed_flow_count`, with `recent_user_agents` so you can judge
   them). On iOS the Simulator and the host share one network stack, so
   traffic leaves it `unknown` (`host_traffic_indistinguishable`); an Android
   device that cannot be read is `setting_unreadable`. Do not upgrade
   `unknown` to "working" in a summary.
3. A HAR exported without `--capture-bodies` carries previews; its `log.comment`
   says so. Do not present a preview as a full payload.
4. If nothing was captured, distinguish the causes: not attached, pinning, the app
   using a client that ignores the proxy, or simply no traffic yet.

## Troubleshooting

| Symptom | Likely cause |
| --- | --- |
| `consent_required` | flag missing; on a TTY the phrase is also required |
| `proxy_not_running` | `network start` first |
| `mitmdump_required` | install mitmproxy; `autonom doctor` prints the command |
| `physical_device_attach_unsupported` | use an emulator, or configure the Wi-Fi proxy by hand |
| Requests list is empty | not attached, pinning, `URLSession` on iOS, or a Flutter app without an in-app proxy hook |
| App shows network errors after a crash | the device may still point at a dead proxy — `autonom doctor` reports it; run `network detach` |

## Related

- `mobile-session` — sessions and teardown
- `mobile-screen` — correlate a captured response with on-screen state
- `android-debugger-agent`, `ios-debugger-agent`

Referenced files: 1 — file references are metadata, not package contents.

mobile-screen13.3 KB

View saved version →

---
name: mobile-screen
description: Understand and control what is on an Android or iOS screen for AI agents using compact accessibility/UI trees, semantic find/tap, gestures, screenshots, and key/text input via the Autonom CLI.
---

# Mobile Screen (Android + iOS Simulator)

## Purpose

Answer **what is on screen** and **act on it** without relying on pixel-only guessing.

1. Prefer a **compact UI tree** (UI Automator on Android, accessibility tree via idb on iOS).
2. Confirm with a **screenshot** when visual layout matters or the tree is ambiguous.
3. Act with **semantic selectors** first; coordinates only as fallback.

The compact node schema is identical on both platforms, so the same reasoning works
either way. What differs is **which field carries the visible label** — see below.

## CLI

```bash
# Compact tree (meaningful nodes by default)
python3 <autonom-root>/scripts/autonom.py ui tree
python3 <autonom-root>/scripts/autonom.py --platform ios --target <UDID> ui tree
python3 <autonom-root>/scripts/autonom.py ui tree --format outline --interactable   # one indented line per actionable node

# Wait until the screen stops changing (animations, spinners, loading lists)
python3 <autonom-root>/scripts/autonom.py ui wait --settled --timeout-ms 5000       # settled, snapshots, changes, elapsed_ms; exit 1 if never settled

# Find
python3 <autonom-root>/scripts/autonom.py ui find --text "Login" --mode contains     # Android
python3 <autonom-root>/scripts/autonom.py ui find --desc "Log In" --mode exact       # iOS
python3 <autonom-root>/scripts/autonom.py ui find --resource-id com.apple.settings.general

# Act
python3 <autonom-root>/scripts/autonom.py ui tap --desc "Continue"
python3 <autonom-root>/scripts/autonom.py ui tap --x 540 --y 1600
python3 <autonom-root>/scripts/autonom.py ui type "user@example.com"
python3 <autonom-root>/scripts/autonom.py ui swipe --from 200,600 --to 200,200
python3 <autonom-root>/scripts/autonom.py ui key KEYCODE_BACK      # Android
python3 <autonom-root>/scripts/autonom.py ui key HOME              # iOS

# Evidence
python3 <autonom-root>/scripts/autonom.py screenshot --out /tmp/screen.png
python3 <autonom-root>/scripts/autonom.py record start --name login
python3 <autonom-root>/scripts/autonom.py record stop

# Offline parse of a dump (CI / fixtures) — the platform is detected from the file
python3 <autonom-root>/scripts/autonom.py ui tree --dump tests/fixtures/ui_dump.xml
python3 <autonom-root>/scripts/autonom.py ui tree --dump tests/fixtures/idb_describe_all_sample.json
```

## Compact node schema (identical on both platforms)

```json
{
  "ref": "n5",
  "role": "button",
  "text": null,
  "desc": "General",
  "resource_id": "com.apple.settings.general",
  "bounds": [16, 380, 386, 432],
  "clickable": true,
  "long_clickable": false,
  "checkable": false,
  "enabled": true
}
```

## Where the visible label lives

**This is the one asymmetry that matters.**

| Platform | Visible label lands in | Select with |
| --- | --- | --- |
| Android | `text` (and `desc` for content-description) | `--text`, `--desc`, `--resource-id` |
| iOS | **`desc`** (from `AXLabel`); `text` comes from `AXValue` and is often `null` | **`--desc`**, `--resource-id` |

On iOS a control labelled "General" has `desc: "General"` and `text: null`, so
`ui find --text "General"` returns **zero matches**. Read the tree first and select
by `--desc`, or by `--resource-id` when the app sets accessibility identifiers.
Flutter on Android puts its labels in `desc` (content-description) too. On both
platforms, when a `--text` miss would have matched a `desc`, the response says so
with a `label_is_in_desc` warning whose hint is the `--desc` retry. A `ui find` or
`ui tap` with no selector at all is `selector_required`. Whitespace is normalised
(iOS joins a merged Flutter label with a newline, Android with a space), except
in `--mode regex`.

## What is on screen, and which match is picked

iOS lists Flutter's scroll cache — nodes laid out but not on screen — at a 0x0
frame. Such a node stays in `ui tree` (it is counted) marked `visible: false`,
and so does any node entirely outside the app's frame, live or from `--dump`.

- `ui tap` and `ui find` resolve **on-screen matches first**: duplicates and
  `--index` are counted among them, exactly as a flow counts, so a selector
  that is unique on Android is not ambiguous on iOS because of an off-screen
  copy. Only when no match is on screen is every match counted.
- `ui tap` on a node that was never laid out on screen refuses with
  `element_offscreen` (with `ref`, `bounds`, `match_count`) and taps nothing —
  it used to tap the screen's origin and report success. Swipe it into view,
  then select it again.
- `ui find --all` lists on-screen matches first, then off-screen ones. Every
  match `ui find` reports, with or without `--all`, carries `index` — the
  `--index` that selects it (`null` for an off-screen match no index reaches
  while another match is on screen). Position *i* of the `--all` list is what
  `--index i` taps.
- `ui wait --settled` says the tree was "still changing" only when its last
  snapshot was a change. If it changed and then held still for less than
  `--quiet-ms`, it says that (`stable_ms`); if only one dump fit in
  `--timeout-ms` (an Android dump can take ~2 s), it says settling could not
  be confirmed and how far to raise it.

## Which tool touched the screen (`backend`)

Every input payload (`ui tap`, `ui swipe`, `ui type`, `ui key`) names its
`backend`: `adb` on Android; `idb` or `axe` on iOS. The iOS tree always comes
from idb. On Xcode 27 an idb companion built before the fix cannot load
SimulatorKit, so the tree works while input fails with
`ios_hid_framework_missing`; with AXe installed
(`brew install cameroncooke/axe/axe`) the default `--ios-hid auto` sends
input through AXe instead. `--ios-hid idb|axe` (or `AUTONOM_IOS_HID`) pins a
route, and `--axe PATH` (or `AUTONOM_AXE`) names the binary; both go before
the verb. `autonom doctor` reports `ios_tree` and `ios_hid` separately with
the fix for each. On Xcode 27 with AXe carrying input, `ios_hid.ready` stays
`false` (idb's own HID) while `ios_hid.axe_ready` and `ios_ui.ready` are
`true`: read `ios_ui` and `axe_ready` — input works, do not give up on it.

## Gestures and keys by platform

| Verb | Android | iOS |
| --- | --- | --- |
| `ui tap`, `ui swipe`, `ui type` | yes | yes |
| `ui pinch`, `ui rotate`, `ui shake` | refused with `unsupported_on_platform` | refused with `unsupported_on_platform` |
| `ui key` | `KEYCODE_*` | `HOME`, `LOCK`, `SIDE_BUTTON`, `SIRI`, `APPLE_PAY`, or a numeric HID code |

**No platform has pinch, rotate, or shake.** Android's `input` cannot express
them and idb has no such command either, so both are refused rather than faked.
Use `ui swipe` for anything reachable by a drag; rotation and shake need a hand
on the Simulator window (Device > Rotate).

iOS has no global Back button; tap the navigation bar's back control instead.

**Typing needs focus.** `ui type` sends keystrokes to whatever has keyboard
focus; with no focused field the characters vanish while the command still
exits 0. The response carries `focused` (the node that will receive the text) with
`focus: verified` on Android or `unverified` on iOS — idb's dump has no focus
attribute, so on iOS the check degrades to "a text field is on screen" — or a
`no_focused_field` warning. Tap the field, wait for it, type, then confirm
with `ui find` on the typed text. In flows, `inputText` refuses to type into
nothing (`flow_no_focused_field`).

## Coordinate space

iOS accessibility frames and `idb ui tap` both use **points**, not pixels. Never
multiply by the display scale. A tap computed outside the target's reported screen
rectangle is refused with `coordinate_space_mismatch` rather than dispatched — on a
3x device a pixel mix-up would otherwise land silently in the wrong place and make
the agent report a defect that does not exist.

## Deterministic captures

Two screenshots of the same screen differ by the battery glyph and the
signal bars unless the status bar is pinned — and a before/after comparison
then reports a change the app never made. Pin it once per session, on either
platform, before the first capture you intend to compare. The clock stays
real, because evidence should say when it was taken; the marketing 9:41 is
one key away:

```bash
python3 <autonom-root>/scripts/autonom.py simulator status-bar pin        # full battery, full signal, no notification icons, real clock
python3 <autonom-root>/scripts/autonom.py simulator status-bar pin --value hhmm=0941   # Android: 9:41 via demo mode (clock frozen); iOS: time=9:41
python3 <autonom-root>/scripts/autonom.py simulator status-bar clear      # restore the live bar when done
```

On Android the default pin never enters SystemUI demo mode, because demo mode
freezes the clock at the moment it is entered; `hhmm=`, `wifi=`, `mobile=`, or
`mode=demo` opt into it when a fixed clock or shaped Wi-Fi bars matter more
than a ticking one. `values.mode` in the response says which path ran.
The emulator's signal cannot be read back, so a live Android pin carries a
`signal_unstable` warning and does not claim the signal as verified. The
emulator's bars and battery icon drift within a minute of any pin, so
`autonom screenshot` and flow captures re-send the live pin right before the
frame (`repinned: true` in the response) — take evidence through them, not a
raw `adb screencap`. `status-bar clear` restores the battery and signal state recorded before the
first pin.

A pin is bound to the emulator it was set on (AVD name and boot id): an
emulator that took the same serial later — another AVD, or a reboot — never
receives it. The record is dropped instead (`stale_pin_dropped: true` and a
warning), and `devices shutdown` / `devices boot` drop it outright; pin again
on the new device. On API 36 emulators SystemUI may not draw the battery
glyph at all even though `dumpsys battery` reports the pinned level, so a
missing battery icon in a capture is the emulator's, not the pin's or the
app's.

On Android, turn system animations off before comparing captures, and wait
for the screen to settle:

```bash
python3 <autonom-root>/scripts/autonom.py simulator animations pin        # window, transition, animator scales to 0
python3 <autonom-root>/scripts/autonom.py ui wait --settled
python3 <autonom-root>/scripts/autonom.py simulator animations reset      # restores what the first pin replaced
```

iOS has no host-level animation switch (`unsupported_capability`).
Every `simulator` control answers `verified: true` only after reading the
state back; otherwise `verified: false` with the reason in `verification`.

`ui type` on iOS is at the mercy of autocorrect: a non-English keyboard
rewrote "Sync conflicts when editing offline" into something else mid-flow.
Pin the keyboard and locale on the **shut-down** simulator before typed text
must be exact (`reboot=true` lets the verb cycle a booted one):

```bash
python3 <autonom-root>/scripts/autonom.py simulator keyboard pin --value locale=en-US --value reboot=true
python3 <autonom-root>/scripts/autonom.py simulator keyboard show          # pinned: true|false, per key; backup: true while pinned
python3 <autonom-root>/scripts/autonom.py simulator keyboard reset         # restores the values pin replaced
```

`pin` snapshots the keys it overwrites (a region-override locale, a keyboard
setting a human chose) and `reset` puts them back, so a shared simulator
leaves a test the way it entered it. Always pair a pin with a reset.

Android has no host-level keyboard store (the settings live inside Gboard), so
`simulator keyboard` refuses there with `unsupported_capability`; turn
suggestions off in the field or the keyboard app instead.

Every `screenshot` (and `shots show`) reports `width` and `height` from the
PNG itself. On iOS that is **pixels** while the tree and `ui tap` speak
points; on Android it is whatever the AVD's screen is. State the space you
computed in when you report a coordinate.

## Agent workflow

1. `session start` so the target and artifacts are explicit.
2. `ui tree` → read labels, roles, enabled state. Note which field holds the label.
3. `ui find` / `ui tap` for the next action; after a navigation, `ui wait
   --settled` before the next read.
4. `screenshot` after state changes that need visual proof — **compare before/after**;
   an exit code of 0 does not prove the screen changed. Pin the status bar first
   so the diff shows only what the app changed.
5. Re-dump the tree after navigation; never reuse stale refs.
6. Report **measured** on-screen text and tree facts separately from hypotheses.

## Boundaries

- The iOS tree is the **accessibility** hierarchy, not the SwiftUI/UIKit view tree.
  Its quality depends on the app's labelling.
- A tree with fewer than three meaningful nodes is reported with
  `sparse_accessibility_tree`. That means "the app exposes little", not "the screen
  is empty" — add `Semantics` (Flutter) or `.accessibilityLabel` /
  `.accessibilityIdentifier` (SwiftUI), or fall back to a screenshot.
- When no element carries an identifier, `no_accessibility_identifiers` says so, so
  a zero-match `--resource-id` query is not mistaken for a missing control.
- System dialogs (permissions) need the same selectors or coordinates.
- Browser mirror (`android-emulator-browser`) is visual support, not a substitute
  for the tree.

## Related

- `mobile-session`
- `mobile-network` — correlate on-screen errors with HTTP traffic
- `android-debugger-agent`, `ios-debugger-agent`

Referenced files: 1 — file references are metadata, not package contents.

mobile-session9.81 KB

View saved version →

---
name: mobile-session
description: Own an explicit Android device or iOS simulator session for AI agents — list targets, start Autonom session artifacts, install/launch/force-stop/clear apps, and stop cleanly with evidence directories.
---

# Mobile Session (Android + iOS Simulator)

## Purpose

Give the agent a **single explicit target** and an artifact directory before UI,
log, or network work. Prefer this skill over ad-hoc `adb` or `simctl` when testing
or debugging.

One verb set covers both platforms. `--serial` remains a permanent Android alias
of `--target`, so existing Android workflows are unchanged.

## CLI entrypoint

```bash
python3 <autonom-root>/scripts/autonom.py devices          # Android + iOS in one list
python3 <autonom-root>/scripts/autonom.py doctor           # what this machine can actually do
```

Each device carries `running` (true when Booted / `device`), so you can tell a
live target from a cold one without knowing each platform's wording. The
Android listing also reports `avds` (bootable emulator images not yet started)
and `avd_profiles` — each AVD's hardware profile, screen size and density, and
API level read from its `config.ini` — so "the phone-sized emulator" is a
lookup, not a guess from the name. A running emulator names the `avd` it
booted from.

`doctor` lists every active `AUTONOM_*` override under `overrides` and warns
with `override_path_missing` when one points at a binary that does not exist —
the usual reason a tool reads as missing while `which` finds it.

### Boot and shut down a target

```bash
python3 <autonom-root>/scripts/autonom.py devices boot --avd Pixel_9      # start an Android emulator, wait for boot
python3 <autonom-root>/scripts/autonom.py devices boot --udid <UDID>      # boot an iOS simulator
python3 <autonom-root>/scripts/autonom.py devices shutdown --serial emulator-5554
python3 <autonom-root>/scripts/autonom.py devices shutdown --udid <UDID>
```

`boot --avd` waits for `sys.boot_completed` and returns the serial it came up
as (`--no-wait` to skip the wait). `session start` still boots a shutdown
simulator on its own, so `devices boot` is only needed to start an Android
emulator up front or to pre-warm a target. `shutdown` refuses any serial that
is not `emulator-<port>` — it never powers off physical hardware.

An emulator started by `devices boot` is registered as **harness-owned**:
`doctor` does not list it as an orphan while it runs, and `devices shutdown`
releases its registry entry, so no stale process warning follows.

### Android

```bash
python3 <autonom-root>/scripts/autonom.py session start --serial emulator-5554 --app-id com.example.app
python3 <autonom-root>/scripts/autonom.py session launch com.example.app            # resume where it was
python3 <autonom-root>/scripts/autonom.py session launch com.example.app --fresh    # launcher activity on a cleared task
python3 <autonom-root>/scripts/autonom.py session launch com.example.app --fresh --activity .MainActivity
python3 <autonom-root>/scripts/autonom.py session force-stop com.example.app
python3 <autonom-root>/scripts/autonom.py session clear com.example.app
python3 <autonom-root>/scripts/autonom.py session stop
```

### iOS Simulator

```bash
python3 <autonom-root>/scripts/autonom.py session start \
  --platform ios --target <UDID> \
  --install build/ios/iphonesimulator/Runner.app \
  --launch --app-id com.example.app --log-stream

python3 <autonom-root>/scripts/autonom.py session launch com.example.app --setenv FLAVOR=staging
python3 <autonom-root>/scripts/autonom.py session force-stop com.example.app
python3 <autonom-root>/scripts/autonom.py session stop
```

Target flags work before or after the subcommand. A shutdown simulator is booted
automatically and the response reports `"booted": true` when this call did it.
The response also says what `--install` and `--launch` did: `installed`
(true/false) and `launched` (`null`, or the app id with the iOS `pid` / the
Android `am start -W` report: `mode`, `component`, `launch`). An Android
`session launch` (resume) is the launcher icon's own `am start -W` intent,
not `monkey`, so it no longer resets a pinned orientation.

`session start` refuses with `session_already_active` (naming the live
session's id and target) while a session is current — run `session stop`
first; it never replaces a session silently. If `--install` or `--launch`
fails, the new session is rolled back (`session_rolled_back` names it) so no
half-built session is left current. A flag that does nothing on the
platform (`--log-stream` or `--setenv` on Android, `--activity` on iOS) is
reported in a `flag_ignored_on_platform` warning, and `session uninstall`
reports a failed uninstall as `ok: false`.

Intent extras that start with `--` must be attached to the flag, or argparse
reads them as options: `session launch com.example.app --arg=--es --arg=key=value`.
Argument mistakes come back as `error_code: usage_error` with the usage line.

All commands print JSON. `session start` writes, **machine-globally** under
`~/.autonom/sessions/` (honouring `AUTONOM_HOME`) — not in the project, so the
active session is found from any directory, like mocks and per-app knowledge:

```text
~/.autonom/sessions/<session_id>/{shots,trees,logs,network,recordings,crashes,files,journal.ndjson,session.json}
~/.autonom/sessions/<session_id>/flows/<run_id>/events.ndjson   # per-step flow run evidence (mobile-flow)
~/.autonom/sessions/current.json
```

### Session journal — the full record of a run

Everything a session does is appended to `<session>/journal.ndjson`: every verb
(what ran, its scrubbed arguments, success, the artifact produced) and any note
the agent writes. Read it back for analytics or a handoff:

```bash
python3 <autonom-root>/scripts/autonom.py journal                     # whole timeline
python3 <autonom-root>/scripts/autonom.py journal --kind note         # only notes
python3 <autonom-root>/scripts/autonom.py journal --verb 'ui tap'     # only taps
python3 <autonom-root>/scripts/autonom.py note add "login screen renders; password field visible" --task login
python3 <autonom-root>/scripts/autonom.py note list --task login
python3 <autonom-root>/scripts/autonom.py journal --session-id <id>   # a finished session's timeline
```

The journal is secret-safe (typed text, secret-bearing flag values in any
spelling or abbreviation, and every `KEY=VALUE` option value are masked) and
best-effort (a journal error never fails your command). `ui tree` keeps a
sequenced file per capture under `trees/`, so the whole run's screens are kept,
not just the last.

## Clearing app data

| Platform | Behavior |
| --- | --- |
| Android | `pm clear` — full data reset |
| iOS default | uninstall + reinstall; needs the `.app` path, so start the session with `--install` |
| iOS `--strategy privacy` | `simctl privacy reset all` — **permissions only, app data survives** |

Without a recorded install path the verb fails with
`ios_clear_requires_install_path` rather than silently doing something weaker.

## Remote iOS targets

`idb` splits into a macOS companion and a client that can run elsewhere, so a
Linux orchestrator can drive a Mac:

```bash
export AUTONOM_IDB_COMPANION=mac-farm-01:10882
# or: --idb-host mac-farm-01 --idb-port 10882
```

## Rules

1. If more than one target is ready, **always** pass `--target` (or `--serial` / `--udid`).
   Ambiguity is an error listing the candidates, never a silent guess.
2. Do not print signing secrets, tokens, or `.env` values while installing/launching.
3. One session per investigation; stop when done so artifacts stay coherent.
   `autonom session outputs` lists every followable file in the session dir
   (device log stream, process output, network flows) with a `tail -f` hint
   for a human's second terminal, plus the `metrics/` and `recordings/`
   artifacts (`followable: false` for a binary, `directory: true` for an
   Instruments `.trace`, with an `open` hint where the host has one).
4. `session stop` tears down the log stream, recorder, and proxy best-effort,
   then every process the session registered (the iOS log-stream writer, a
   `canvas serve` pair, an idb companion its own idb calls started), and
   reports each action under `teardown` — it never fails because teardown
   failed. Every pid is signalled only once its registry row or its command
   line shows it is still that process (a pid in a session file can be days
   old); one `ps` cannot vouch for is skipped with `unverified_skipped`. A
   companion it did not start is named in a `companion_left_running`
   warning, never killed.
5. If a session died without stopping, `autonom doctor` lists orphaned processes,
   any device left pointing at a dead proxy, and any emulator still routed
   through another session's *live* proxy (`device_attached_to_foreign_proxy`).

## Failure codes worth knowing

| Code | Meaning |
| --- | --- |
| `ambiguous_target` | more than one ready target; pass `--target` |
| `session_already_active` | a session is current; `session stop` it before starting another |
| `idb_required` | iOS `ui` verbs need idb; `screenshot`/`logs`/`open` still work |
| `ios_boot_failed` | simulator never reached `Booted`; try `xcrun simctl erase <udid>` |
| `no_active_session` | start one with `session start` |
| `usage_error` | argparse rejected the argv; `hint` carries the usage line |
| `app_not_debuggable` | `file ls`/`pull` need a debuggable build; release and system apps refuse run-as |
| `simulator_must_be_shutdown` | `simulator keyboard pin` needs a shut-down simulator; pass `--value reboot=true` or shut it down first |
| `simulator_data_not_found` | the simulator has no data directory yet — boot it once, or set `AUTONOM_CORESIMULATOR_DEVICES` |

## Related skills

- `mobile-screen` — UI tree, find, tap, screenshot
- `mobile-network` — HTTP(S) capture and mocking
- `ios-debugger-agent` / `android-debugger-agent` — build-and-run workflows
- `project-router` — when to load runtime skills at all

Referenced files: 1 — file references are metadata, not package contents.

project-router4.34 KB

View saved version →

---
name: project-router
description: Route Flutter, native Android, native iOS, and device test/debug tasks to the smallest relevant Autonom skill set after inspecting the repository stack and runtime boundary.
---

# Project Router

## Purpose

For the whole-system map — what Autonom can do, how it works, the end-to-end
device loop — read the `autonom` skill first. This router is the dispatcher that
narrows that map to the smallest stack-specific skill set for the task at hand.

Classify the repository and task before changing code or driving a device.
A Flutter app contains `pubspec.yaml` with a Flutter SDK dependency. A native
Android project normally starts from Gradle settings and Android modules. A native
iOS project starts from `*.xcodeproj`, `*.xcworkspace`, or `Package.swift`.
Hybrid repositories can contain several, so route by the layer being changed.

For **runtime QA / UI debug** on a device, emulator, or simulator, load
`mobile-session` and `mobile-screen` (Autonom CLI) in addition to the
stack-specific debugger. Load `mobile-network` when the task mentions an API,
request, response, mock, HAR, proxy, or "why is this call failing". Load
`mobile-flow` when the task mentions a repeatable flow, regression check,
smoke suite, replaying a journey, or a `.yaml` flow file.

Once the target app is known (a package/bundle id is in play), load
`mobile-memory` and read `~/.autonom/apps/<package>/app.md` and any matching
`flows/` runbook **before** driving the device — it carries the backend, network
model, schema locations, and step-by-step flows worked out on earlier runs, so
the same digging is not repeated. Record a new flow there only when it is
durable, reusable, and evidence-backed.

Use the repository-selected Flutter SDK for executable commands. When selection
is ambiguous or FVM is pinned, resolve it without silently falling back:

```bash
python3 <marketplace-root>/scripts/flutter_exec.py --root . -- --version
```


## Routing workflow

1. Inspect repository documentation and the project root.
2. Run the shared toolchain snapshot when build layout or SDK state is unclear:

   ```bash
   python3 <plugin-root>/../../scripts/toolchain_snapshot.py . --execute
   ```

3. Detect the primary path:

   - Flutter feature, Dart code, widgets, navigation, tests, or package work:
     load the relevant `flutter-*` skills.
   - Android manifest, Gradle, Kotlin, platform channels, app links,
     notifications, permissions, or plugin native code inside a Flutter app:
     load `flutter-android-platform` plus the narrow Android skill.
   - Pure Kotlin or Compose project: load the native Android/Compose skills.
   - `*.xcodeproj`, `*.xcworkspace`, `Package.swift`, or work inside a Flutter
     app's `ios/` directory (plist, capabilities, Swift/Obj-C module, signing
     configuration): load `ios-project-setup`, and `ios-debugger-agent` when the
     app must actually run.
4. Load runtime skills only when device evidence is required:
   - session + screen: `mobile-session`, `mobile-screen` via `scripts/autonom.py`
   - per-app knowledge and flows: `mobile-memory` (read `~/.autonom/apps/<package>/`)
   - deeper debug: `android-debugger-agent` / `ios-debugger-agent` / `flutter-debugger-agent`
   - network inspect or mock: `mobile-network`
   - visual mirror: `android-emulator-browser`
5. When the machine's capabilities are unclear, run
   `python3 <autonom-root>/scripts/autonom.py doctor` — it reports which platforms
   are usable and what is missing, instead of discovering it mid-task.
6. Do not load every skill pre-emptively; progressive loading keeps the task
   focused.

## Default verification ladder

For Flutter:

```bash
flutter pub get
dart format --output=none --set-exit-if-changed <changed-paths>
flutter analyze <changed-paths-or-project>
flutter test <narrow-test-path>
```

For native Android:

```bash
./gradlew <narrow-task> --console=plain
```

Broaden checks when shared architecture, generated code, Gradle configuration,
platform integration, or release behavior changed.

## Boundaries

- Flutter UI behavior should be proven through widget or integration tests and
  a running Flutter app, not inferred from Android views alone.
- Native permission dialogs, platform views, notifications, and system surfaces
  can require UI Automator, Espresso, Patrol, or manual device evidence.
- Performance and memory claims need comparable runtime captures.

Referenced files: 1 — file references are metadata, not package contents.

toolchain-doctor1.29 KB

View saved version →

---
name: toolchain-doctor
description: Inspect Flutter, Dart, Gradle, Android Gradle Plugin, Kotlin, SDK levels, Java, adb devices, and repository-pinned toolchain configuration without asserting stale latest versions.
---

# Toolchain Doctor

Use before project creation, SDK upgrades, Gradle migrations, broken builds, or
any claim about installed tool versions.

## Snapshot

```bash
python3 <repository-or-plugin-root>/scripts/toolchain_snapshot.py . --execute --json
```

Declarations first; optional local command execution. Never labels a version as
“latest”. Re-check `references/official-sources.md` for migrations.

## Read before editing

- `pubspec.yaml`, `.fvmrc`, `.fvm/`, `analysis_options.yaml`
- Android Gradle settings/modules, catalogs, wrapper, `gradle.properties`
- CI scripts and documented commands
- `flutter --version --machine`, `flutter doctor -v`, `dart --version`
- `./gradlew --version`, `java -version`, `adb devices -l`

Prefer `fvm flutter` when the repo is FVM-pinned and `fvm` is available.

## Policy

- Re-check official docs for version-sensitive work.
- Keep toolchain migrations separate from feature work.
- Do not paste a generic template over a live project.
- Do not upgrade the world to fix one conflict.
- Report exact observed versions and the compatibility error text.

Referenced files: 2 — file references are metadata, not package contents.

Package details

Publisher declarations from the archived package. These are separate from our research and the live service's terms.

Package license
MIT
Package author
Andrei Iatsuk
Keywords
See keywords

Declared capabilities

  • Interactive
  • Read
  • Write

Package observed Nov 3, 2026.

Technical details
First seen
Oct 24, 2026 · 12:40 UTC
Last seen
Nov 3, 2026 · 18:00 UTC
Collection status
Collected

plugins_6abcb949170081919055247eaf18cfc0

Download plugin data (JSON)