DuckTerm Field Notes

A 21h 54m goal-driven engineering story

D’iOS-only à Android-ready

Une expérience d'adaptation multiplateforme presque entièrement automatique : de l'écran noir de Ghostty, de l'aphasie de Mosh, au détective d'autorisation de Chrome, à la liste Google Play, à la boucle fermée du produit RevenueCat et aux trois appareils "témoignant" les uns des autres.

  • Goal-driven
  • iOS → Android
  • Ghostty + Mosh
  • Play + RevenueCat
  • Evidence first
Passer du terminal iOS au terminal Android Le téléphone de gauche et la tablette de droite sont reliés par un pont de code, de tests et de preuves. iOS / Ghostty $ ssh duck@host PARITY_READY scroll · ime · tui Android / Ghostty $ mosh duck@host SAME_SESSION_OK pixelcopy · pip · pad GOAL fail-closed first 403 ≠ Jour du jugement dernier evidence or it didn’t happen
21h54mObjectif logique, le temps nécessaire n'est pas la disponibilité d'un seul processus
4,441Modèle d'appel d'outil, pas de numéro de test indépendant
3API35、Pixel API30、Xiaomi Pad
30/30L'instantané final de la suite connectée n'est pas ajouté à l'ancien instantané
259Appels d'inspection visuelle dans la fenêtre Objectif

Tout est parti d’une demande apparemment simple : malgré toutes les bonnes choses qu’iOS possède déjà, Android devrait également les avoir, n’est-ce pas ? Si cette phrase est confiée à la gestion de projet traditionnelle, elle deviendra une rangée de Jira ; remis à cet objectif, il deviendra un terminal natif Ghostty, Mosh Real UDP, Pad grand écran, PiP, Widget, notifications, Google Play et RevenueCat - plus un canard qui apprend progressivement à regarder des captures d'écran, à ouvrir un navigateur, à vérifier 403 et à douter de ses propres preuves.

01 / THE CONTRACT

Android n'est pas iOS et porte un manteau vert

Le moyen le plus courant de commettre des erreurs dans ce que l'on appelle « l'adaptation d'iOS à Android » est de copier la capture d'écran de la page pixel par pixel. Ce qu'il faut vraiment migrer, c'est le contrat de produit : l'utilisateur ne peut saisir un caractère chinois qu'une seule fois sur le terminal distant ; SSH ne peut pas être déconnecté si le moteur de rendu échoue ; le nouveau cadre doit être vu au retour de l'arrière-plan ; le forfait en cours doit être reconnu pour la récupération de l'abonnement ; le Pad n’est pas un téléphone portable agrandi horizontalement.

Ainsi, chaque fonctionnalité est traduite en trois couches : sémantique partagée, mise en œuvre de la plateforme et limite des preuves. iOS peut utiliser sa propre primitive de fenêtre d'affichage et Android peut utiliser Ghostty ABI 3 ; mais "doigt pour parcourir l'historique plus ancien et cliquez pour revenir en direct" doit être cohérent.

flowchart LR
  I["iOS a déjà la capacité"] --> C{"contrat de produit partagé"}
  C --> A["Implémentation native d'Android"]
  A --> D["Stratégie de différenciation des plateformes"]
  D --> V["Trois appareils et vérification du service réel"]
  V --> R{"Des preuves passées ?"}
  R -- "Non" --> F["Localisez la cause profonde et affinez la réclamation"]
  F --> V
  R -- "Oui" --> S["Examen par mini-lots et soumission atomique"]
            
Figure 1 : Au lieu de copier l'implémentation, migrez le contrat. L’échec reviendra à la boucle de vérification au lieu de la version « qui semble à peu près la même ».
⌨️

Entrez le contrat

La composition de l'IME reste locale et engagée une seule fois ; les touches physiques, Ctrl, Alt et le modificateur collant ne peuvent pas être sérialisés.

🖼️

contrat de rendu

La cellule large, la sélection, l'instantané, le défilement et le curseur doivent être cohérents sur l'image réelle.

🛟

contrat échoué

GL ne parvient pas à couper xterm, mais le transport SSH, l'historique des anneaux et les entrées utilisateur restent vivants.

💳

contrat commercial

Le paiement mensuel, le paiement annuel et la durée de vie sont cartographiés de manière cohérente dans Play et RevenueCat. Lorsque la configuration fait défaut, il vaut mieux ne pas vendre plutôt que de deviner le SKU.

Le multiplateforme ne consiste pas à « se ressembler », mais à « tenir la même promesse même en cas de problème ».

02 / THE NATIVE LINE

Laissez Ghostty échouer d'abord, puis laissez-le réussir

La première règle d'Android Ghostty est un peu contre-intuitive : ne commencez jamais avec optimisme . Les constantes synchrones sont toujours définies en premier sur `ok=false`. L'auto-test asynchrone doit prouver que le noyau natif, ABI, VT, EGL, le shader, le téléchargement de texture, le dessin et la relecture sont tous vrais avant de passer de xterm à Ghostty.

Cela évite la métaphysique mobile classique du « obscurcir d'abord et ensuite en parler » dans la première image. Il existe également une limite supérieure de 2,5 secondes pour la certification de santé : si le GPU veut vraiment entrer en méditation, l'utilisateur peut regarder le spinner pendant 2,5 secondes maximum, puis obtenir un xterm fonctionnel au lieu d'un blanc permanent de type Zen.

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
            
Figure 2 : Le moteur de rendu est une surface remplaçable, pas un transport. Même si GL échoue pendant le fonctionnement, la session continue.
GHOSTTY_HEALTH
core.ok=true
gl.maxRgbDelta=2

$ printf '中文 😀 é'
中文 😀 é

same session
FORCED_GL_FAIL
renderer unhealthy
→ xterm fallback

$ echo STILL_ALIVE
STILL_ALIVE

connect count: 1
MOSH_ROAM
Wi-Fi off
DURING_OK
Wi-Fi on
AFTER_OK

same PID · same UDP

Le film terminal est une représentation de la restauration de l'article, et non l'image originale de l'arrière-plan de l'opération ; les marqueurs, les résultats et les limites proviennent de preuves d'équipement réelles.

Ces os durs qui s’assemblent « facilement »

Composition de la méthode de saisie, séquence de touches xterm, sélection tactile, copie ActionMode, défilement natif, compositeur d'arrière-plan, sonde PixelCopy, instantané du processeur, mode de mise au point du clavier Pad... chaque élément ressemble à P3 individuellement, mais lorsqu'il est empilé, il détermine "est-ce un terminal ou un rectangle noir clignotant".

03 / THE BROWSER DETECTIVE

Il y a une autorisation dans le navigateur, mais il n'y a pas de clé toute faite

Les autorisations de Play Console se trouvent dans le profil Chrome connecté ; le Playwright intégré a un profil propre et rien ne peut être vu ; Le CDP n'a pas encore été ouvert. La réponse la plus simple est « demander à l’utilisateur de le faire manuellement ». Goal a choisi une autre voie : découvrir d'abord quel contexte de navigateur dispose d'autorisations, puis compresser la participation des utilisateurs en un consentement nécessaire.

Diagramme de détective des autorisations du navigateur L'agent identifie le consentement de Chrome via des captures d'écran. Après autorisation de l'utilisateur, CDP reprend la page puis se connecte à gcloud, Play API et RevenueCat. chrome:// remote debugging Autoriser le débogage à distance ? L'utilisateur autorise CONTROL PLANE ✓ CDP snapshot + click ✓ gcloud API enablement ✓ Play publisher SA ✓ RevenueCat v2 secrets stay outside repo snapshot then click
Figure 3 : page de contrôle CDP, ne peut pas contrôler sa propre fenêtre contextuelle d'autorisation. Les captures d'écran et l'automatisation du système d'exploitation sont responsables de l'obtention du consentement, et les utilisateurs sont responsables de la confiance.

S'il n'y a pas de pont prêt à l'emploi, générez-en un dans le répertoire temporaire : SDK MCP + `chrome-devtools-mcp` + client léger stdio, puis utilisez PTY persistant pour envoyer en continu `instantané → clic → instantané`. Il n'est pas présenté comme une « capacité de plate-forme » et est nettoyé une fois terminé ; la conclusion véritablement réutilisable est un profil dédié, une liste d'autorisation d'origine, une version corrigée et une désensibilisation par défaut.

Une blague : 403 ne signifie pas nécessairement que Google ne vous aime pas. Il peut s'agir d'une portée OAuth, d'une autorisation GCP IAM, d'une autorisation Play App, d'une autorisation de facturation, ou l'autorisation est toujours en cours. Couchez d'abord et réessayez ; n'utilisez pas le bouton d'actualisation comme un chapelet.
sequenceDiagram
  actor U as utilisateur
  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: Nous avons constaté qu'il existe déjà un état de connexion et un contexte de compte cible.
  U->>B: Autoriser explicitement le débogage à distance
  B-->>A: Contrôle de page CDP disponible
  A->>G: Activez l'API et créez deux ensembles d'identités les moins privilégiées
  A->>B: Lier les autorisations des applications dans la Play Console
  A->>P: Créez un catalogue de manière idempotente et téléchargez l'AAB interne
  A->>R: Créer une application Android et une cartographie des produits
  P-->>A: état des traces et des artefacts
  R-->>A: droit et statut de l'offre
  A-->>U: Demander uniquement des faits commerciaux et des décisions de publication irréversibles
            
Figure 4 : Le navigateur, Cloud IAM, les autorisations Play et RevenueCat sont les quatre plans de contrôle. Les mélanger en un seul « Connexion réussie » ne donnera que plus de 403.
04 / THE EVIDENCE LAB

La capture d’écran est noire, n’écrivez pas encore d’éloge funèbre pour le GPU.

Le moment le plus dangereux pour la vérification d’un téléphone Android est celui où la première capture d’écran semble très convaincante. SurfaceView peut être complètement noir dans les captures d'écran du système ; La capture d'écran Xiaomi peut faire 0 octet ; screenrecord ne peut enregistrer que le monde sans couche matérielle. À l'inverse, lorsque le tmux distant voit le marqueur, cela prouve seulement que le transport est vivant, mais pas que le compositeur local l'a dessiné.

Laboratoire de preuves à trois équipements L'émulateur API35, Pixel 4 et Xiaomi Pad sont respectivement responsables du nouveau système, de l'ancien système, de la machine réelle et de la vérification OEM sur grand écran. API35 emulator Nouveau contrat API · Pixel 4 API30 physical IME · FCM · SQLite Xiaomi Pad API33 · MIUI master detail Pad · Surface · rotation
Figure 5 : Les trois appareils ne sont pas le même test multiplié par trois, mais trois hypothèses complémentaires : nouvelle API, ancienne machine réelle et grand écran OEM.

Par conséquent, les preuves sont conçues comme plusieurs oracles : marqueur unique, arborescence d'interface utilisateur, PixelCopy, raster CPU, PID, dumpsys, logcat et volet distant. Ils doivent se falsifier les uns les autres, pas s'aligner et applaudir.

flowchart TB
  M["Injecter un marqueur unique"] --> U["Arborescence de l'interface utilisateur et état du contrôle"]
  M --> X["Capture d'écran ou PixelCopy"]
  M --> S["PID · dumpsys · logcat"]
  M --> T["Statut SSH/Mosh/tmux distant"]
  U --> C{"Plusieurs oracles sont-ils cohérents ?"}
  X --> C
  S --> C
  T --> C
  C -- "Non" --> A["Classification des artefacts de test"]
  A --> N["Remplacez le nonce et l'oracle et réexécutez"]
  N --> M
  C -- "Oui" --> B["Écrire des conclusions limitées"]
            
Figure 6 : Plus il y a de preuves, mieux c'est, mais plus la source est indépendante, mieux c'est. En cas de conflit, doutez d'abord de la chaîne de collecte, puis doutez du produit, et doutez également de votre premier jugement.
phénomène observéJe suis presque arrivé à la mauvaise conclusionDernière preuve orthogonale utilisée
Capture d'écran du Pad tout noirLe compositeur Ghostty se bloque à nouveauPixelCopy + CPU bitmap + generation
le marqueur apparaît deux foisLa saisie exactement une fois échoueLe nouveau nonce prouve que le lot de délai d'attente chevauche la réexécution
Wi-Fi déconnecté et restauréMosh termine son itinérance cross-NATDéclarez uniquement la même session à restaurer ; le véritable itinérance dépend de l'identité du réseau
Métro démarrage à froid ANRLe délai de rendu de 2,5 secondes est bloquéAucune récurrence du démarrage à froid du bundle intégré/mis en cache
05 / THE LONG GOAL

La vérité sur « l’automatisation totale » : ce n’est pas qu’il n’y a personne, mais que les gens n’apparaissent que là où ils ont de la valeur

La durée logique du Goal est cette fois de 21 heures, 54 minutes et 42 secondes, mais la disponibilité de la machine est plus courte. Il ne s'agit pas d'un processus mystérieux qui insiste pour ne pas dormir, mais s'appuie sur Git, tmux, les outils d'interrogation, l'état du plan, le transfert de révision et l'état du système externe pour restaurer en permanence le contexte.

Les utilisateurs de ce cycle ont très peu de participation réelle : connectez-vous à RevenueCat, effectuez un petit nombre de clics dans la console de développement Chrome/Google qui doivent être confirmés par le titulaire du compte et donnez une autorisation explicite pour des actions irréversibles telles que commit/push. La découverte de l'état de connexion, la plupart des formulaires Play et la vérification des faits, l'ouverture de gcloud/autorisation, la génération d'API, la vérification de l'appareil, les tests d'achat/restauration, l'enregistrement de captures d'écran, la récupération et le tri des erreurs sont tous en boucle fermée par Goal lui-même.

flowchart TB
  O["Objectif stable"] --> P["Plan d’étape et seuil de preuve"]
  P --> G["Validation atomique Git"]
  P --> T["tmux et outils interrogeables"]
  P --> E["État de l’appareil et du service externe"]
  P --> R["verdict d'un examinateur indépendant"]
  G --> C["Continuer après le processus ou le changement de démarrage"]
  T --> C
  E --> C
  R --> C
  C --> P
            
Figure 7 : La persistance d'un objectif long vient d'un état récupérable, et non d'un processus qui ne se termine jamais.

Lisez d'abord la machine, puis écrivez le code

Le navigateur, la CLI, l'appareil, l'état de connexion, le contrat iOS existant et l'état sale de l'entrepôt sont tous identifiés en premier.

Garantissez d'abord une issue

Ghostty, Mosh, Billing et OTA/native skew définissent tous la direction de l'échec avant d'activer de nouvelles fonctionnalités.

Laissez les vrais appareils et services parler

Les tests connectés sont suivis de véritables couches SSH, UDP, FCM, Play, RevenueCat et Pixel.

Chaque ligne à risque passe individuellement par la sentinelle

Les P1/P2 ne sont pas en retard jusqu’au bout ; l'examinateur accepte le code, puis la preuve de l'appareil.

Soumission précise de la liste blanche

L'arborescence de travail parallèle n'est pas entraînée et les profils temporaires, les supports, les luminaires et les secrets sont nettoyés à temps.

06 / THE SURPRISE LEDGER

Dix choses que la différence finale ne vous dit pas

🕵️

Trouvez vous-même l'entrée d'autorisation

Déterminez quel compte peut fonctionner à partir du contexte Chrome existant au lieu d'obliger l'utilisateur à recompter l'environnement.

📸

Si vous n'avez pas CDP, prenez d'abord une capture d'écran.

Lorsque la couche OS obtient le consentement, l'utilisateur n'effectue l'autorisation nécessaire qu'une seule fois, puis repasse à l'automatisation DOM.

🧰

Si vous manquez d'outils, fabriquez les plus petits outils

Génération sur site de MCP, catalogue Play, téléchargement AAB et rapprochement des clients RevenueCat.

🔐

Deux SA, pas de clé principale

La facturation et l'éditeur sont séparés, et les autorisations GCP IAM et Play App sont hiérarchiques.

🧯

Un échec forcé ne fera pas perdre la session

Le moteur de rendu plante et la surface est modifiée, tandis que le transport et l'historique continuent de fonctionner.

🎞️

Si la vidéo est piratée, les preuves seront remplacées

N’utilisez pas une chaîne de collecte brisée pour tirer des conclusions sur un produit.

🧪

DEV Seam réussit à ne pas contrefaire les produits

L'injection de débogage passe toujours par le gestionnaire de produit/la porte native, et DEV Pro est acheté de manière inappropriée comme preuve.

🧭

Affiner activement la déclaration

La récupération Wi-Fi est une récupération et non une usurpation d'identité d'une itinérance NAT différente.

🧹

Le nettoyage, c’est aussi une prestation

Les captures d'écran, les vidéos, les profils, les appareils, la base de données, la rotation et les états de la méthode de saisie sont restaurés.

🪶

Rendre la complexité réutilisable

Les analyses JSONL, CDP, les preuves de périphériques, le bootstrap Play/RC sont tous précipités dans des manuels.

07 / THE REUSABLE PLAYBOOK

Que peut-on copier directement depuis la prochaine application ?

Goal-driven Porting, v1

  1. Lorsque vous rédigez un contrat, n’écrivez pas « Faites comme iOS ». Clarifiez le succès, l’échec, le repli et la sémantique visible par l’utilisateur.
  2. Trouvez d’abord l’autorité. Qui a le dernier mot sur l'état de connexion du navigateur, Cloud IAM, les autorisations de stockage, les appareils et les services.
  3. Premier échec de fermeture. Les nouveaux biais natifs, de facturation et OTA doivent avoir d’anciens chemins qui peuvent fonctionner.
  4. Si vous manquez d'outils, réalisez des ponts fins. Seul le dernier kilomètre est résolu et le défaut est temporaire, observable et destructible.
  5. Testez l’équipement par hypothèse. Les émulateurs, les vieilles machines réelles et les grands écrans OEM ont tous leurs propres tâches, pas des jeux de quantité.
  6. Marqueur unique + plusieurs oracles. Le pixel, la structure, le système, le transport et l'extrémité distante doivent avoir au moins deux authentifications mutuelles.
  7. Considérez 4xx comme une carte. Portée, IAM, autorisation d'application, positionnement hiérarchique des autorisations de facturation.
  8. Chaque limite de danger est examinée individuellement. Fermez P1/P2 par petits lots et exécutez à nouveau le service réel.
  9. Présentez-vous lorsque vous avez besoin d’une vraie personne. Ce tour n'a en fait qu'une connexion, quelques clics de consentement/console et une autorisation d'effet secondaire git ; si d’autres projets impliquent une MFA, des faits juridiques ou une production, ces frontières irremplaçables seront laissées à d’autres.
  10. Nettoyez et notez les limites. Ne laissez pas de secrets, de montages, de « passes » exagérées et d'histoires héroïques irremplaçables.

La dernière conclusion contre-intuitive

L’histoire véritablement communicable de l’ingénierie de l’IA n’est pas « elle a fonctionné pendant 22 heures d’affilée », mais « elle savait quand le faire toute seule, quand rechercher des preuves, quand admettre que les captures d’écran ne sont pas dignes de confiance et quand demander aux gens de cliquer dessus ». La limite supérieure de l’automatisation n’est pas déterminée par le nombre de clics, mais par le sens des limites.

Le meilleur agent ne prend pas toutes les décisions à votre place ; cela réduit votre participation aux quelques moments où une décision est vraiment nécessaire.