Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
name: tests

# The suite guards things that are easy to break by hand and invisible in review:
# that every core guide's cross-references resolve, that the curator can still see
# the `generalizable` tags in the shipped cards, and that no unreviewed curator
# draft block reaches core/. All of that only helps if it runs without being asked.
on:
push:
branches: [main]
pull_request:

permissions:
contents: read

jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
# Stdlib-only, no install step. 3.9 is the verified floor; 3.13 is current.
# The pair catches syntax that quietly needs a newer interpreter than a
# contributor happens to be running.
python-version: ["3.9", "3.13"]

steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

# Separate steps, each running even if an earlier one failed, so a single
# run reports every regression instead of only the first. `!cancelled()`
# rather than `always()`: a cancelled run should stop, not push on. The job
# still fails if any step fails.
- name: Repo structure
run: python tests/test_structure.py

- name: Curator
if: ${{ !cancelled() }}
run: python tests/test_curate.py
Comment on lines +40 to +42

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Run later test steps after an earlier failure

When Repo structure fails in this workflow, GitHub Actions' default success condition skips the subsequent Curator and report-only steps, so this does not achieve the comment's stated goal of reporting the other failures independently. Add an always() condition to the later diagnostic steps (while preserving the job failure) so multiple regressions are surfaced in one run.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 4360085. The comment described behaviour the workflow didn't have: success() gating meant a run surfaced only the first regression.

Later steps now carry if: ${{ !cancelled() }} rather than always(), so a cancelled run stops instead of pushing on. The job still fails if any step fails.


- name: Curator report-only run leaves the tree clean
# test_curate covers this against fixtures; this runs it against the real
# repo, where a stray write would land in a tracked guide.
if: ${{ !cancelled() }}
run: |
python scripts/curate.py --harness . --out "$RUNNER_TEMP/curator-report"
if [ -n "$(git status --porcelain)" ]; then
echo "::error::curate.py modified the working tree without --apply"
git status --porcelain
exit 1
fi

# The single stable name to mark as a required check in branch protection.
# Requiring the matrix jobs directly means their names ("test (3.9)") are baked
# into repo settings: drop a Python version and its required check never
# reports again, blocking every merge until someone with admin notices. This
# job's name never changes, so the matrix stays free to change.
gate:
needs: test
if: ${{ !cancelled() }}
runs-on: ubuntu-latest
steps:
- name: Require the test matrix to have succeeded
# `needs.test.result` is the matrix's aggregate: success only if every leg
# succeeded. Checking it explicitly is what makes the gate fail on a
# skipped or cancelled matrix, which `needs:` alone would let through
# given the `!cancelled()` condition above.
run: |
echo "test matrix result: ${{ needs.test.result }}"
[ "${{ needs.test.result }}" = "success" ] || exit 1
19 changes: 16 additions & 3 deletions .gitignore

@RasulOs RasulOs Sep 6, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I already said, or add memory/ into local or remove local/. In general, I agree that scripts/ and tests/ could be removed from .gitignore but don't add same script/test files you added in previous commits

Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,21 @@ credentials/**
!credentials/README.md
!credentials/TEMPLATE.md

# User-owned local overlay. Mirrors a tracked path to override or extend it
# without ever touching a tracked file, so `git pull --ff-only` keeps working.
local/**
!local/
!local/README.md

tmp/

# Local QA helpers and experiments. The harness product is Markdown-only.
scripts/
tests/
# Curator reports are regenerable. Run scripts/curate.py to recreate them.
.curator/

# Secrets, just in case.
.env

# scripts/ and tests/ are tracked: the curator and its test suite ship with the
# harness. Only their build artifacts are ignored.
scripts/__pycache__/
tests/__pycache__/
19 changes: 11 additions & 8 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,18 @@ Do not import local drivers directly for ordinary agent work.
dependencies. Agents should still import only `mobilerun_core`.
Skip the pip step if the libraries are pinned. If offline, continue with the current version. On any other failure, read `UPDATE.md`.
2. Decide the target platform before acting.
3. For Android work, read `platforms/android/GUIDE.md`.
4. For iOS work, read `platforms/ios/GUIDE.md`.
5. Do not load all files.
6. When the foreground app id is known, read only that app card if it exists:
3. Read `core/mobile-ux-primitives/GUIDE.md` before observing an unfamiliar screen. It applies to both platforms and belongs above the platform split, not inside it — read it once you know a screen is coming, before the platform guide's own instructions.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reading core/mobile-ux-primitives/GUIDE.md before observing an unfamiliar screen will make agent much slower. Remove

4. For Android work, read `platforms/android/GUIDE.md`.
5. For iOS work, read `platforms/ios/GUIDE.md`.
6. Do not load all files.
7. When the foreground app id is known, read only that app card if it exists:
- Android: `apps/android/<package>/CARD.md`
- iOS: `apps/ios/<bundle-id>/CARD.md`
7. Read platform recovery only after a control, setup, state, or connectivity failure.
8. Read the credentials guide under `core/credentials` when a screen asks for login, API keys, OTP, 2FA, payment, passcode, or other secrets.
9. Write to `credentials/<app-id>.md` only when the user explicitly asks for local credential files.
10. Read `core/memory/GUIDE.md` before reading or writing files under `memory/`.
Then read the same path under `local/` if it exists (`local/apps/android/<package>/CARD.md`, `local/apps/ios/<bundle-id>/CARD.md`). That file is the user's own, and it wins wherever it disagrees with the shipped card. It may be the only card that exists — the user's private or internal apps live there. Read `local/README.md` before writing anything under `local/`.
8. Read platform recovery only after a connectivity, setup, or state-extraction failure. For an in-app action that didn't produce the expected result, or a dialog/permission prompt covering the screen, read `core/debugging/GUIDE.md` or `core/blockers/GUIDE.md` first — those are not connectivity problems.
9. Read the credentials guide under `core/credentials` when a screen asks for login, API keys, OTP, 2FA, payment, passcode, or other secrets.
10. Write to `credentials/<app-id>.md` only when the user explicitly asks for local credential files.
11. Read `core/memory/GUIDE.md` before reading or writing files under `memory/`.

## Non-Negotiables

Expand All @@ -37,6 +39,7 @@ Do not import local drivers directly for ordinary agent work.
- Stop on credentials, payment, or destructive consent. Continue only if the user explicitly authorized the exact action; otherwise ask the user.
- Store durable operational facts or useful information for the subsequent runs in `memory/` only after reading `core/memory/GUIDE.md`.
- Store credentials in `credentials/` only if the user explicitly asks for local credential files.
- Treat `local/` as user-owned and authoritative: it outranks the tracked file it mirrors. Write there only when the user asks for a local customization, and never move its content into a tracked file without asking — it is deliberately not shared.

## Platform Routing

Expand Down
37 changes: 35 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,12 +127,16 @@ device.start_app("com.android.settings")
Skill-based runtimes can load `SKILL.md`; all runtimes should start with
`AGENTS.md`. It routes agents to the smallest needed file:

- `core/mobile-ux-primitives/GUIDE.md` before observing an unfamiliar screen — cross-platform, read before the platform split.
- `platforms/android/GUIDE.md` for Android work.
- `platforms/ios/GUIDE.md` for iOS work.
- `platforms/<platform>/recovery/GUIDE.md` only when control fails.
- `core/debugging/GUIDE.md` or `core/blockers/GUIDE.md` for an in-app action failure or a dialog covering the screen.
- `platforms/<platform>/recovery/GUIDE.md` only when a connectivity/setup/state-extraction failure occurs.
- the credentials guide under `core/credentials` only when a credential or human-gated screen appears.
- `core/memory/GUIDE.md` only when reading or writing local agent-owned memory.
- `core/learn-from-tutorial/GUIDE.md` when the current screen turns out to be the app's own tutorial or onboarding walkthrough.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this line

- `apps/android/<package>/CARD.md` or `apps/ios/<bundle-id>/CARD.md` only for the foreground app.
- the same path under `local/` after any tracked file it loads — your own copy, which wins on conflict. See [Customising Cards Without Merge Conflicts](#customising-cards-without-merge-conflicts).

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this line

- `UPDATE.md` only when the session-start `git pull --ff-only` fails.


Expand Down Expand Up @@ -172,4 +176,33 @@ two apart.

## Local State

`memory/` and `credentials/` are local, ignored folders. The repository tracks only their rules/templates. Agents may write operational memory after reading `core/memory/GUIDE.md`.
`local/`, `memory/`, and `credentials/` are local, ignored folders. The repository tracks only their rules/templates.

| Folder | Written by | Weight |
| --- | --- | --- |
| `local/` | you | authoritative — the agent obeys it and never shares it |
| `memory/` | the agent, after reading `core/memory/GUIDE.md` | provisional — re-verified before use |

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All these problems are solved if you add memory/ into local/

| `credentials/` | you, and only if you ask for local credential files | secrets; see the guide under `core/credentials` |

## Customising Cards Without Merge Conflicts

Session start runs `git pull --ff-only`, so editing a tracked file breaks your
next update. Put your version under `local/` at the same path instead:

```text
apps/android/com.google.android.gm/CARD.md # shipped, tracked
local/apps/android/com.google.android.gm/CARD.md # yours, wins on conflict
local/apps/android/com.acme.internal/CARD.md # yours only — private/internal apps
```

The agent reads the shipped card first, then yours, and yours wins where the
two disagree. If only yours exists, it simply is the card — which is where
internal builds and private apps belong.

`local/` is gitignored except its README, so the pull keeps fast-forwarding
even when upstream changes a card you have overridden. Cards are found by path,
so there is no index to update. `scripts/curate.py` does not read `local/`,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove it

so nothing personal leaks into a shared promotion.

Full details in `local/README.md`. Note that `git clean -xdf` deletes ignored

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not related to mobile-harness, remove it

files, `local/` included.
14 changes: 10 additions & 4 deletions SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,15 @@ local backends.
## Load Order

1. Read `AGENTS.md`.
2. Read `platforms/android/GUIDE.md` for Android work.
3. Read `platforms/ios/GUIDE.md` for iOS work.
4. Read recovery, credentials, memory, and app-card files only when routed
there by `AGENTS.md` or the platform guide.
2. Read `core/mobile-ux-primitives/GUIDE.md` before observing an unfamiliar screen — cross-platform, load it before the platform split below.
3. Read `platforms/android/GUIDE.md` for Android work.
4. Read `platforms/ios/GUIDE.md` for iOS work.
5. Read recovery, credentials, memory, and app-card files only when routed
there by `AGENTS.md` or the platform guide. For an in-app action failure or
a dialog/permission prompt (not a connectivity failure), that means
`core/debugging/GUIDE.md` or `core/blockers/GUIDE.md`.
6. After any tracked file you load, read the same path under `local/` if it
exists. That is the user's own copy and it wins on conflict. See
`local/README.md`.

For setup and runtime registration, read `install.md`.
8 changes: 8 additions & 0 deletions apps/android/com.ebay.mobile/CARD.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,19 @@ targets eBay on Android.
- Prefer `find_nodes`, `tap_node`, and resource ids over fixed coordinates.
- Verify the result sort before collecting data; visually plausible results can
still be sorted by a default ranking.
- Results use infinite scroll. Keep scrolling until the collected count stops
growing; there is no next-page control. <!-- generalizable: infinite-scroll-no-pagination -->

## Traps

- Sort and filter bottom sheets can be delayed in the accessibility tree after
opening. Re-observe before deciding an option is absent.
- The active sort option is often omitted from the sort sheet, which lists only
the other choices. Absence can mean "already selected", not "not available".
- eBay can resume on a previous screen. Confirm the current page before
searching, sorting, or scraping.
- Search result cards can repeat similar titles. Verify the intended listing
opened after tapping.
- Currency and marketplace depend on the device/account storefront.
- If eBay asks to sign in, add a payment method, or verify identity, stop and
read `core/credentials/GUIDE.md`.
11 changes: 11 additions & 0 deletions apps/android/com.google.android.gm/CARD.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,19 @@ Use this card only when Gmail is the foreground package or the task explicitly t
- After launching, wait for inbox or account picker before acting.
- If Gmail asks to add an account, sign in, or verify identity, stop and read `core/credentials/GUIDE.md`.

## Compose

- `compose_button` opens the composer. Fields: `peoplekit_autocomplete_chip_group` (To), `subject`, `editor` (body). Sent recipients show as `peoplekit_chip` buttons.
- The body `editor` is **not clickable** — tap `composearea_tap_trap_bottom` to focus it. That places the caret at position 0, so `key('delete')` there is a no-op; tap directly on the text to edit the end.
- Add recipients one at a time: re-resolve the empty `EditText` inside the chip group each time (it moves as chips wrap), tap it, type the address, then type `,` to chip it.
- Navigate up (`Navigate up` in `compose_toolbar`) saves the draft; verify it under drawer → `Drafts`.

## Traps

- **`type()` goes to whatever field is actually focused.** If the body is not focused, an entire body silently appends to the subject with no error. Read the destination field back after every write.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove it. It is not related to gm but a general skill in mobilerun-core-local

- **`clear_input()` does not clear the body** (rich text) and has been seen to clear the *subject* while `editor` reported `is_focused=True`. Do not trust it to target the field you think you are in.
- A contacts-permission dialog (`Allow Gmail to access your contacts?`) and a `Help me write` smart-features bottom sheet can appear mid-typing and swallow keystrokes. Decline both (`DON'T ALLOW`, `No thanks`) — neither is needed to compose — then re-verify what was typed.

@RasulOs RasulOs Sep 6, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this line. User can need it

- Tapping a fixed coordinate in the To row hits an existing chip and opens a contact sheet or chip popup menu (`Remove the recipient`); `key('back')` backs out.
- Inbox rows can have repeated text; verify the opened message subject after tapping.
- Search results can lag. Observe again before acting on the first result.
- Do not store email contents in memory unless the user explicitly asks.
22 changes: 22 additions & 0 deletions apps/android/com.instagram.android/CARD.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Instagram Card

Package: `com.instagram.android`

Use this card only when Instagram is the foreground package or the task explicitly targets Instagram.

## Useful Labels

- Home, Search, Reels, and Profile are often the bottom nav tabs.
- The heart icon under a post toggles like state; a filled/colored heart means already liked.
- The paper-plane icon opens the share sheet for a post.

## Flow Notes

- The home feed loads more posts automatically near the bottom of the scroll; there's no "load more" button — keep scrolling until new posts stop appearing. <!-- generalizable: infinite-scroll-no-pagination -->
- Double-tapping a post image likes it — equivalent to tapping the heart once from an unliked state.
- Stories, if present, are a horizontally scrollable row above the feed, distinct from the vertically scrolling feed below.

## Traps

- Double-tapping an already-liked post does not unlike it — only the heart icon reliably toggles both directions.
- If Instagram asks to log in, verify a code, or confirm a phone number, stop and read `core/credentials`.
21 changes: 21 additions & 0 deletions apps/android/com.reddit.frontpage/CARD.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Reddit Card

Package: `com.reddit.frontpage`

Use this card only when Reddit is the foreground package or the task explicitly targets Reddit.

## Useful Labels

- Up/down arrows next to a post or comment are vote controls; the count between them is net score, not a rating.
- A top-left menu icon often opens community/navigation options; a magnifying glass opens search.

## Flow Notes

- Subreddit and post feeds auto-load additional content near the bottom of the scroll; there is no numbered pagination control. <!-- generalizable: infinite-scroll-no-pagination -->
- Tapping an active vote arrow again returns it to neutral rather than flipping straight to the opposite vote — expect two taps to reverse a vote.
- Comment threads nest by indentation; a "N more replies" control often replaces a fully expanded thread.

## Traps

- Vote counts can lag briefly after tapping; re-observe rather than assuming the tap failed if the score doesn't change instantly.
- If Reddit asks to log in or verify an account, stop and read `core/credentials`.
9 changes: 9 additions & 0 deletions apps/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,12 @@ apps/ios/<bundle-id>/CARD.md
```

Cards are plain Markdown, not `SKILL.md`, so generic agents do not auto-load every app. Each card should stay focused on stable app-specific facts: package or bundle id, useful selectors, common flows, navigation structure and traps.

Cards here are tracked and shared. A user's own card goes at the same path under `local/`, which is gitignored:

@RasulOs RasulOs Sep 6, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why we have local/, memory/ and credentials/ all gitignored? Put them all under local/


```text
local/apps/android/<package>/CARD.md
local/apps/ios/<bundle-id>/CARD.md
```

Read the tracked card first, then the `local/` one; the `local/` one wins where they disagree, and may be the only one that exists. There is nothing to add to this file for either — cards are discovered by path, which is what keeps a local card from ever conflicting with a `git pull`. See `local/README.md`.
Loading
Loading