It started from a seemingly simple request: for all the good things that iOS already has, Android should also have them, right?If this sentence is handed over to traditional project management, it will grow into a row of Jira; handed over to this Goal, it will grow into Ghostty native terminal, Mosh real UDP, Pad large screen, PiP, Widget, notifications, Google Play and RevenueCat - plus a duck that gradually learns to look at screenshots, open a browser, check 403, and doubt its own evidence.
Android is not iOS wearing a green jacket
The most common way to make mistakes in the so-called "adaptation from iOS to Android" is to copy the screenshot of the page pixel by pixel.What really needs to be migrated is the product contract: the user can only enter a Chinese character once to the remote end; SSH cannot be disconnected if the renderer fails; the new frame must be seen when returning from the background; the current plan must be recognized for subscription recovery; the Pad is not a mobile phone that is enlarged horizontally.
So each capability is translated into three layers: shared semantics, platform implementation, and evidence boundary.iOS can use its own viewport primitive, and Android can use Ghostty ABI 3; but "finger down to browse the older history and click to return to live" must be consistent.
flowchart LR
I["iOS already has the ability"] --> C{"shared product contract"}
C --> A["Android native implementation"]
A --> D["Platform differentiation strategy"]
D --> V["Three devices and real service verification"]
V --> R{"Evidence passed?"}
R -- "no" --> F["Locate the root cause and narrow the claim"]
F --> V
R -- "yes" --> S["Mini-batch review and atomic submission"]
Enter the contract
IME composition remains local and committed exactly once; physical keys, Ctrl, Alt, and sticky modifier cannot be serialized.
rendering contract
Wide cell, selection, snapshot, scrollback and cursor must be consistent on the real frame.
failed contract
GL fails to cut xterm, but SSH transport, ring history and user input remain alive.
business contract
Monthly payment, annual payment, and lifetime are mapped consistently in Play and RevenueCat. When configuration is lacking, it is better not to sell than to guess the SKU.
Cross-platform is not about "looking the same", but "keeping the same promise even when something goes wrong".
Make Ghostty fail before letting it succeed
The first rule of Android Ghostty is a bit counter-intuitive: never start out optimistic.Synchronous constants are always set to `ok=false` first. The asynchronous self-test must prove that native core, ABI, VT, EGL, shader, texture upload, draw and readback are all true before switching from xterm to Ghostty.
This avoids the classic mobile metaphysics of "dark it first and then talk about it" in the first frame.There is also a 2.5-second upper limit for health certification: if the GPU really wants to enter meditation, the user can watch the spinner for up to 2.5 seconds, and then get a working xterm instead of a Zen-like permanent blank.
stateDiagram-v2
[*] --> Pending
Pending --> Ghostty: health proof succeeds before timeout
Pending --> Xterm: timeout or proof fails
Ghostty --> Xterm: runtime GL failure
Xterm --> StableSession: same transport and ring replay
Ghostty --> StableSession: native renderer active
StableSession --> [*]
note right of Ghostty
Native VT plus full GL path
end note
core.ok=true
gl.maxRgbDelta=2
$ printf '中文 😀 é'
中文 😀 é
renderer unhealthy
→ xterm fallback
$ echo STILL_ALIVE
STILL_ALIVE
Wi-Fi off
DURING_OK
Wi-Fi on
AFTER_OK
The terminal film is a representation of the article restoration, not the original image of the operation background; markers, results and boundaries are from real equipment evidence.
Those hard bones that are "easily" put together
Input method composition, xterm key sequence, touch selection, ActionMode copy, native scrollback, background compositor, PixelCopy probe, CPU snapshot, Pad keyboard focus mode... each item looks like P3 individually, but when stacked together, it determines "is this a terminal or a flashing black rectangle".
The browser had access, but no ready-made key
The permissions of Play Console are in the logged-in Chrome profile; the built-in Playwright is a clean profile and nothing can be seen; CDP has not been opened yet.The easiest answer is "ask the user to do it manually".Goal chose another path: first find out which browser context has permissions, and then compress user participation into a necessary consent.
If there is no ready-made bridge, generate one in the temporary directory: MCP SDK + `chrome-devtools-mcp` + thin stdio client, and then use persistent PTY to continuously send `snapshot → click → snapshot`.It is not packaged as a "platform capability" and is cleaned up after completion; the truly reusable conclusion is a dedicated profile, origin allowlist, fixed version and default desensitization.
sequenceDiagram
actor U as user
participant A as Goal Agent
participant B as Chrome
participant G as gcloud
participant P as Play Publisher API
participant R as RevenueCat API
A->>B: Found that there is already a login state and target account context
U->>B: Explicitly allow remote debugging
B-->>A: CDP page control available
A->>G: Enable the API and create two sets of least privileged identities
A->>B: Bind app permissions in Play Console
A->>P: Create catalog idempotently and upload internal AAB
A->>R: Create Android app and product mapping
P-->>A: track and artifact status
R-->>A: entitlement and offering status
A-->>U: Request only business facts and irreversible release decisions
A black screenshot is not a GPU obituary
The most dangerous moment for Android phone verification is when the first screenshot looks very convincing.SurfaceView may be completely black in system screenshots; Xiaomi screencap may be 0 bytes; screenrecord may only record the world without hardware layer.Conversely, when the remote tmux sees the marker, it only proves that the transport is alive, but not that the local compositor drew it.
Therefore, the evidence is designed as multiple oracles: unique marker, UI tree, PixelCopy, CPU raster, PID, dumpsys, logcat, and remote pane.They must falsify each other, not line up and applaud.
flowchart TB
M["Inject unique marker"] --> U["UI tree and control status"]
M --> X["Screenshot or PixelCopy"]
M --> S["PID · dumpsys · logcat"]
M --> T["Remote SSH/Mosh/tmux status"]
U --> C{"Are multiple oracles consistent?"}
X --> C
S --> C
T --> C
C -- "no" --> A["Classifying test artifacts"]
A --> N["Replace nonce and oracle and rerun"]
N --> M
C -- "yes" --> B["Write bounded conclusions"]
| phenomenon seen | Almost came to the wrong conclusion | Last used orthogonal evidence |
|---|---|---|
| Pad screenshot all black | Ghostty compositor hangs again | PixelCopy + CPU bitmap + generation |
| marker appears twice | Entering exactly-once fails | The new nonce proves that the timeout batch overlaps with the rerun |
| Wi-Fi disconnected and restored | Mosh completes cross-NAT roam | Only declare the same session to be restored; the real roam depends on the network identity |
| Metro cold start ANR | 2.5 seconds renderer defer stuck | No recurrence of embedded/cached bundle cold start |
The truth about ‘fully automatic’: people appear only where they add value
The logical duration of the Goal this time is 21 hours, 54 minutes and 42 seconds, but the machine uptime is shorter.It is not a mysterious process that insists on not sleeping, but relies on Git, tmux, pollable tools, plan status, review handoff, and external system status to continuously restore context.
Users in this round have very little actual participation: log in to RevenueCat, complete a small number of clicks in the Chrome/Google Developer Console that must be confirmed by the account holder, and give explicit authorization for irreversible actions such as commit/push.Login status discovery, most Play form filling and fact checking, gcloud/permission opening, API generation, device verification, purchase/restoration testing, screenshot recording, error recovery and sorting are all closed loop by Goal itself.
flowchart TB
O["Stable Goal goal"] --> P["Stage plan and evidence threshold"]
P --> G["Git atomic commit"]
P --> T["tmux and pollable tools"]
P --> E["Device and external service status"]
P --> R["independent reviewer verdict"]
G --> C["Continue after process or boot change"]
T --> C
E --> C
R --> C
C --> P
Read the machine first, then write the code
The browser, CLI, device, login state, existing iOS contract and warehouse dirty state are all identified first.
Guarantee a way out first
Ghostty, Mosh, Billing, and OTA/native skew all define the direction of failure before enabling new capabilities.
Let real devices and services do the talking
Connected tests are followed by real SSH, UDP, FCM, Play, RevenueCat and Pixel layers.
Each high-risk line passes through the sentinel individually
P1/P2 are not backlogged to the end; the reviewer accepts the code and then the device evidence.
Accurate whitelist submission
The parallel work tree is not entrained, and temporary profiles, media, fixtures and secrets are cleaned up in time.
Ten things the final diff does not tell you
Find the permission entrance yourself
Determine which account can operate from the existing Chrome context instead of requiring the user to re-account the environment.
If you don’t have CDP, take a screenshot first.
When the OS layer reaches consent, the user only performs the necessary authorization once, and then switches back to DOM automation.
If you lack tools, make the smallest tools
On-site generation of MCP, Play catalog, AAB upload and RevenueCat reconcile clients.
Two SAs, no master key
Billing and publisher are separated, and GCP IAM and Play App permissions are hierarchical.
Forced failure will not lose the session
The renderer crashes and the surface is changed, while the transport and history continue to work.
If the video is hacked, the evidence will be replaced
Don’t use a broken collection chain to draw conclusions about a product.
DEV seam succeeds in not counterfeiting products
Debug injection still goes through the product manager/native gate, and DEV Pro is improperly purchased as evidence.
Actively narrow the statement
Wi-Fi recovery is recovery, not impersonation of a different NAT roam.
Cleaning is also delivering
Screenshots, videos, profiles, fixtures, DB, rotation and input method states are restored.
Make complexity reusable
JSONL forensics, CDP, device evidence, Play/RC bootstrap are all precipitated into manuals.
What can the next app copy?
Goal-driven Porting, v1
- When writing a contract, don’t write “Do as iOS does.”Clarify success, failure, fallback, and user-visible semantics.
- Find the authority first.Who has the final say on browser login status, Cloud IAM, store permissions, devices and services.
- First fail-closed.New native, billing, and OTA skew must have old paths that can work.
- If you lack tools, make thin bridges.Only the last mile is solved, and the default is temporary, observable, and destructible.
- Test the equipment by hypothesis.Emulators, old real machines, and OEM large screens all have their own tasks, not quantity games.
- Unique marker + multiple oracles.Pixel, structure, system, transport and remote end must have at least two mutual authentications.
- Think of 4xx as a map.Scope, IAM, App permission, Billing permission hierarchical positioning.
- Each danger boundary is reviewed individually.Close P1/P2 in small batches and run the real service again.
- Show up when you need a real person.This round actually only has login, a few consent/console clicks, and git side-effect authorization; if other projects involve MFA, legal facts, or production, these irreplaceable boundaries will be left to others.
- Clean up and write down the boundaries.Leave no secrets, fixtures, exaggerated “passes” and unrepeatable heroic stories.
The last counter-intuitive conclusion
The truly communicable AI engineering story is not "it worked for 22 hours straight", but "it knew when to do it on its own, when to look for evidence, when to admit that screenshots are not trustworthy, and when to have people click on it."The upper limit of automation is not determined by the number of clicks, but by the sense of boundaries.
The best Agent does not make every decision for you; it compresses your involvement to the few moments that truly require a call.