-
Notifications
You must be signed in to change notification settings - Fork 35
Add core/mobile-ux-primitives, learn-from-tutorial, debugging, blockers, the curator, and a local/ overlay #15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
dcadb8f
e8244a4
99a7d01
db6b2fd
1bce476
0b6354a
6ffa161
92dbecf
2e0c199
80d6e20
4360085
35e48db
740f508
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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 | ||
|
|
||
| - 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 | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
|---|---|---|
|
|
@@ -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. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Reading |
||
| 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 | ||
|
|
||
|
|
@@ -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 | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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). | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. | ||
|
|
||
|
|
||
|
|
@@ -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 | | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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/`, | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is not related to mobile-harness, remove it |
||
| files, `local/` included. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. | ||
| 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`. |
| 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`. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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: | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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`. | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When
Repo structurefails in this workflow, GitHub Actions' default success condition skips the subsequentCuratorand report-only steps, so this does not achieve the comment's stated goal of reporting the other failures independently. Add analways()condition to the later diagnostic steps (while preserving the job failure) so multiple regressions are surfaced in one run.Useful? React with 👍 / 👎.
There was a problem hiding this comment.
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 thanalways(), so a cancelled run stops instead of pushing on. The job still fails if any step fails.