DuckTerm Field Notes

A 21h 54m goal-driven engineering story

iOS 専用から Android 対応へ

ほぼ完全に自動化されたクロスプラットフォーム適応実験: Ghostty の黒い画面、Mosh の失語症から、Chrome の許可探偵、Google Play のリスト、RevenueCat 製品のクローズド ループ、および 3 つのデバイスの相互の「証言」まで。

  • Goal-driven
  • iOS → Android
  • Ghostty + Mosh
  • Play + RevenueCat
  • Evidence first
iOS端末からAndroid端末へクロスブリッジ 左側の電話と右側のタブレットは、コード、テスト、証拠のブリッジによって接続されています。 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 ≠ ドゥームズデイ evidence or it didn’t happen
21h54m論理的な目標、かかる時間は単一プロセスの稼働時間ではありません
4,441ツール呼び出しパターン、独立したテスト番号ではない
3API35、Pixel API30、Xiaomi Pad
30/30最後の接続されたスイートのスナップショットは古いスナップショットに追加されません
259目標ウィンドウ内の目視検査コール

それは、一見単純な要求から始まりました。iOS にすでに備わっている優れた機能をすべて、Android にも備えるべきではないでしょうか。この文が従来のプロジェクト管理に引き継がれると、一連の Jira に成長します。この目標に引き渡されると、Ghostty ネイティブ ターミナル、Mosh リアル UDP、Pad 大画面、PiP、ウィジェット、通知、Google Play、RevenueCat に成長します。さらに、スクリーンショットを見て、ブラウザを開き、403 をチェックし、自分自身の証拠を疑うことを徐々に学習するアヒルになります。

01 / THE CONTRACT

Android は iOS ではなく、緑色のコートを着ています

いわゆる「iOS から Android への適応」で最もよくある間違いは、ページのスクリーンショットをピクセルごとにコピーすることです。本当に移行する必要があるのは製品契約です。ユーザーはリモート エンドに漢字を 1 回しか入力できません。レンダラーに障害が発生した場合、SSH は切断できません。バックグラウンドから戻るときに新しいフレームが表示される必要があります。サブスクリプションを回復するには、現在のプランが認識される必要があります。 Padは横に拡大する携帯電話ではありません。

したがって、各機能は、共有セマンティクス、プラットフォーム実装、証拠境界の 3 つの層に変換されます。 iOS は独自のビューポート プリミティブを使用でき、Android は Ghostty ABI 3 を使用できます。ただし、「指を下に押して古い履歴を参照し、クリックしてライブに戻る」という動作は一貫している必要があります。

flowchart LR
  I["iOSにはすでにその機能があります"] --> C{"共有製品契約"}
  C --> A["Android ネイティブ実装"]
  A --> D["プラットフォームの差別化戦略"]
  D --> V["3つのデバイスと実際のサービスの検証"]
  V --> R{"証拠は渡されましたか?"}
  R -- "いいえ" --> F["根本原因を特定し、主張を絞り込む"]
  F --> V
  R -- "はい" --> S["ミニバッチレビューとアトミックサブミット"]
            
図 1: 実装をコピーする代わりに、コントラクトを移行します。失敗すると、「ほぼ同じに見える」リリースではなく、検証ループに戻ります。
⌨️

契約書を入力する

IME 構成はローカルに残り、一度だけコミットされます。物理キー、Ctrl、Alt、スティッキー修飾子はシリアル化できません。

🖼️

レンダリング契約

ワイド セル、選択範囲、スナップショット、スクロールバック、カーソルは実際のフレーム上で一貫している必要があります。

🛟

失敗した契約

GL は xterm の切断に失敗しますが、SSH トランスポート、呼び出し履歴、およびユーザー入力は生きたままになります。

💳

業務契約書

毎月の支払い、年間支払い、および生涯は、Play と RevenueCat で一貫してマッピングされます。構成が不足している場合は、SKU を推測するよりも販売しない方が良いでしょう。

クロスプラットフォームとは、「見た目が同じ」ということではなく、「問題が発生した場合でも同じ約束を守る」ということです。

02 / THE NATIVE LINE

Ghostty を最初に失敗させてから、成功させます

Android Ghostty の最初のルールは、少し直観に反しています。決して楽観的に始めてはなりません。同期定数は常に最初に `ok=false` に設定されます。 xterm から Ghostty に切り替える前に、非同期セルフテストでネイティブ コア、ABI、VT、EGL、シェーダー、テクスチャ アップロード、描画、リードバックがすべて正しいことを証明する必要があります。

これにより、最初のフレームで「最初に暗くしてからそれについて話す」という古典的なモバイルの形而上学が回避されます。ヘルス認証には 2.5 秒の上限もあります。GPU が本当に瞑想に入りたい場合、ユーザーはスピナーを最大 2.5 秒間監視し、その後、Zen のような永久的なブランクの代わりに、動作する xterm を取得できます。

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
            
図 2: レンダラーはトランスポートではなく、交換可能なサーフェスです。動作中に GL に障害が発生しても、セッションは継続されます。
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

ターミナルフィルムは記事の修復を表現したものであり、操作背景のオリジナル画像ではありません。マーカー、結果、境界は実際の機器の証拠からのものです。

「簡単に」組み立てられる硬い骨

入力メソッドの構成、xterm キー シーケンス、タッチ選択、ActionMode コピー、ネイティブ スクロールバック、背景コンポジター、PixelCopy プローブ、CPU スナップショット、パッド キーボード フォーカス モード...各項目は個別には P3 のように見えますが、積み上げられると、「これは端末なのか、それとも点滅する黒い四角形なのか」が判断されます。

03 / THE BROWSER DETECTIVE

ブラウザに権限はあるが、既成のキーがない

Play Console の権限は、ログインした Chrome プロフィールにあります。組み込みの Playwright はクリーンなプロファイルであり、何も表示されません。 CDPはまだオープンされていません。最も簡単な答えは、「ユーザーに手動で行うように依頼する」です。 Goal は別の方法を選択しました。まず、どのブラウザ コンテキストに権限があるかを調べてから、ユーザーの参加を必要な同意に圧縮します。

ブラウザ許可検出図 エージェントはスクリーンショットを通じて Chrome の同意を特定します。ユーザーの承認後、CDP がページを引き継ぎ、gcloud、Play API、RevenueCat に接続します。 chrome:// remote debugging リモートデバッグを許可しますか? ユーザーが許可する CONTROL PLANE ✓ CDP snapshot + click ✓ gcloud API enablement ✓ Play publisher SA ✓ RevenueCat v2 secrets stay outside repo snapshot then click
図 3: CDP コントロール ページは、独自の認証ポップアップ ウィンドウを制御できません。スクリーンショットと OS の自動化は同意に達する責任があり、ユーザーは信頼する責任があります。

既製のブリッジがない場合は、一時ディレクトリ (MCP SDK + `chrome-devtools-mcp` + Thin stdio client) にブリッジを生成し、永続的な PTY を使用して `スナップショット → クリック → スナップショット` を連続的に送信します。これは「プラットフォーム機能」としてパッケージ化されておらず、完了後にクリーンアップされます。真に再利用可能な結論は、専用プロファイル、オリジン許可リスト、修正バージョン、およびデフォルトの感度解除です。

悪ふざけ: 403 は、必ずしも Google があなたを愛していないことを意味するわけではありません。 OAuth スコープ、GCP IAM、Play アプリの権限、請求の権限、またはまだ準備中の権限である可能性があります。最初にレイヤーを作成して、もう一度試してください。更新ボタンをロザリオとして使用しないでください。
sequenceDiagram
  actor U as ユーザー
  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: ログイン状態とターゲット アカウント コンテキストがすでに存在することが判明しました
  U->>B: リモート デバッグを明示的に許可する
  B-->>A: CDPページコントロールが利用可能
  A->>G: API を有効にして、最小権限の ID セットを 2 つ作成します。
  A->>B: Play Console でアプリの権限をバインドする
  A->>P: カタログを冪等に作成し、内部 AAB をアップロードする
  A->>R: Android アプリと製品のマッピングを作成する
  P-->>A: トラックとアーティファクトのステータス
  R-->>A: 権利と提供ステータス
  A-->>U: ビジネス上の事実と取り消せないリリースの決定のみを要求してください
            
図 4: ブラウザ、Cloud IAM、Play 権限、および RevenueCat が 4 つのコントロール プレーンです。これらを 1 つの「ログイン成功」に混ぜても、より多くの 403 が生成されるだけです。
04 / THE EVIDENCE LAB

スクリーンショットは黒です。まだ GPU への賛辞を書かないでください。

Android スマートフォンの認証において最も危険な瞬間は、最初のスクリーンショットが非常に説得力があるように見えるときです。システムのスクリーンショットでは、SurfaceView が完全に黒になる場合があります。 Xiaomi スクリーンキャップは 0 バイトである可能性があります。 screenrecord はハードウェア層のない世界のみを記録できます。逆に、リモート tmux がマーカーを認識すると、トランスポートが生きていることが証明されるだけで、ローカル コンポジターがそれを描画したことは証明されません。

3つの機器を備えた証拠実験室 API35エミュレータ、Pixel 4、Xiaomi Padはそれぞれ新システム、旧システム実機、OEM大画面検証を担当します。 API35 emulator 新しいAPI・契約 Pixel 4 API30 physical IME · FCM · SQLite Xiaomi Pad API33 · MIUI master detail Pad · Surface · rotation
図 5: 3 つのデバイスは、同じテストを 3 倍したものではなく、新しい API、古い実機、OEM の大画面という 3 つの補完的な前提条件を組み合わせたものです。

したがって、証拠は、一意のマーカー、UI ツリー、PixelCopy、CPU ラスター、PID、dumpsys、logcat、リモート ペインなどの複数のオラクルとして設計されています。彼らは並んで拍手するのではなく、お互いを偽らなければなりません。

flowchart TB
  M["固有のマーカーを挿入する"] --> U["UIツリーとコントロールステータス"]
  M --> X["スクリーンショットまたはピクセルコピー"]
  M --> S["PID · dumpsys · logcat"]
  M --> T["リモート SSH/Mosh/tmux ステータス"]
  U --> C{"複数のオラクルは一貫していますか?"}
  X --> C
  S --> C
  T --> C
  C -- "いいえ" --> A["テスト成果物の分類"]
  A --> N["nonce と oracle を置き換えて再実行します"]
  N --> M
  C -- "はい" --> B["限定された結論を書く"]
            
図 6: 証拠は多いほど良いですが、情報源の独立性が高いほど良いです。矛盾がある場合は、まずコレクションチェーンを疑い、次に製品を疑い、そして最初の判断も疑います。
見られる現象ほとんど間違った結論に達しそうになった最後に使用された直交証拠
パッドのスクリーンショットが真っ黒Ghostty コンポジターが再びハングアップするPixelCopy + CPU bitmap + generation
マーカーが2回表示される1 回だけ入力すると失敗する新しい nonce は、タイムアウト バッチが再実行と重なっていることを証明します。
Wi-Fiが切断されて復旧しましたMosh がクロス NAT ローミングを完了同じセッションを復元することのみを宣言します。実際のローミングはネットワーク ID に依存します
メトロ コールド スタート ANR2.5 秒レンダラーが遅延してスタックする埋め込み/キャッシュされたバンドルのコールド スタートが再発しない
05 / THE LONG GOAL

「完全自動化」の真実:人がいないのではなく、価値のあるところにだけ人が現れる

今回の目標の論理的な期間は 21 時間 54 分 42 秒ですが、マシンの稼働時間はそれより短くなります。これはスリープしないことを主張する謎のプロセスではなく、Git、tmux、ポーリング可能ツール、計画ステータス、レビューハンドオフ、および外部システムステータスに依存してコンテキストを継続的に復元します。

このラウンドにおけるユーザーの実際の参加はほとんどありません。RevenueCat にログインし、アカウント所有者による確認が必要な Chrome/Google デベロッパー コンソールで少数のクリックを完了し、コミット/プッシュなどの取り消しできないアクションに対する明示的な承認を与えます。ログイン ステータスの検出、ほとんどの Play フォームの入力とファクト チェック、gcloud/権限のオープン、API の生成、デバイスの検証、購入/復元のテスト、スクリーンショットの記録、エラーの回復と並べ替えはすべて Goal 自体によってクローズド ループで行われます。

flowchart TB
  O["安定のゴールゴール"] --> P["段階計画と証拠の閾値"]
  P --> G["Git アトミック コミット"]
  P --> T["tmux とポーリング可能なツール"]
  P --> E["デバイスと外部サービスのステータス"]
  P --> R["独立した査読者の評決"]
  G --> C["プロセスまたはブート変更後に続行する"]
  T --> C
  E --> C
  R --> C
  C --> P
            
図 7: 長い目標の永続性は、決して終了しないプロセスからではなく、回復可能な状態から得られます。

まずマシンを読んでからコードを書きます

ブラウザ、CLI、デバイス、ログイン状態、既存の iOS コントラクト、およびウェアハウスのダーティ状態がすべて最初に特定されます。

まず逃げ道を保証する

Ghostty、Mosh、Billing、および OTA/ネイティブ スキューはすべて、新しい機能を有効にする前に障害の方向を定義します。

実際のデバイスとサービスに話させましょう

接続テストの後には、実際の SSH、UDP、FCM、Play、RevenueCat、Pixel レイヤーが続きます。

各高リスクラインは個別にセンチネルを通過します

P1/P2 は最後までバックログされません。レビュー担当者はコードを受け入れ、次にデバイスの証拠を受け入れます。

正確なホワイトリストの送信

並列ワークツリーは取り込まれず、一時的なプロファイル、メディア、フィクスチャ、およびシークレットは時間内にクリーンアップされます。

06 / THE SURPRISE LEDGER

最終差分では分からない 10 のこと

🕵️

許可の入り口を自分で見つけてください

ユーザーに環境の再アカウントを要求するのではなく、既存の Chrome コンテキストからどのアカウントを操作できるかを決定します。

📸

CDP をお持ちでない場合は、まずスクリーンショットを撮ります。

OS 層が同意に達すると、ユーザーは必要な承認を 1 回だけ実行し、DOM オートメーションに戻ります。

🧰

道具が足りないなら最低限の道具を作ろう

MCP、Play カタログ、AAB アップロード、RevenueCat のオンサイト生成によりクライアントが調整されます。

🔐

2 つの SA、マスターキーなし

請求とサイト運営者は分離されており、GCP IAM と Play App の権限は階層化されています。

🧯

強制的に失敗してもセッションは失われません

レンダラがクラッシュし、サーフェスが変更されますが、トランスポートとヒストリは引き続き機能します。

🎞️

ビデオがハッキングされた場合、証拠は置き換えられます

製品についての結論を引き出すために、壊れた収集チェーンを使用しないでください。

🧪

DEV シームは製品の偽造を防止することに成功

デバッグ挿入は依然としてプロダクト マネージャー/ネイティブ ゲートを通過しており、DEV Pro は証拠として不適切に購入されています。

🧭

ステートメントを積極的に絞り込む

Wi-Fi の回復は回復であり、別の NAT ローミングの偽装ではありません。

🧹

クリーニングも配達しております

スクリーンショット、ビデオ、プロファイル、フィクスチャ、DB、回転、入力メソッドの状態が復元されます。

🪶

複雑さを再利用可能にする

JSONL フォレンジック、CDP、デバイス証拠、Play/RC ブートストラップはすべてマニュアルにまとめられています。

07 / THE REUSABLE PLAYBOOK

次のアプリから直接コピーできるものは何ですか?

Goal-driven Porting, v1

  1. 契約書を書くときは、「iOS のとおりに行う」とは書かないでください。成功、失敗、フォールバック、およびユーザーに見えるセマンティクスを明確にします。
  2. まず権威を見つけてください。ブラウザのログイン ステータス、Cloud IAM、ストアの権限、デバイス、サービスについて最終決定権を持つのは誰です。
  3. 最初のフェイルクローズ。新しいネイティブ、課金、および OTA スキューには、機能する古いパスが必要です。
  4. 工具が足りない場合は細い橋を作ります。最後の 1 マイルのみが解決され、デフォルトは一時的で、観察可能で、破壊可能です。
  5. 仮説に基づいて機器をテストします。エミュレータ、古い実機、OEM の大画面はすべて、量のゲームではなく、独自のタスクを持っています。
  6. ユニークなマーカー + 複数のオラクル。ピクセル、構造、システム、トランスポート、およびリモート エンドには、少なくとも 2 つの相互認証が必要です。
  7. 4xx を地図として考えてみましょう。スコープ、IAM、アプリ権限、請求権限の階層的な位置付け。
  8. それぞれの危険境界は個別に検討されます。 P1/P2 を小さなバッチで閉じて、実際のサービスを再度実行します。
  9. 本当の人が必要なときに現れてください。このラウンドでは実際には、ログイン、数回の同意/コンソールのクリック、および git の副作用の承認のみが行われます。他のプロジェクトに MFA、法的事実、または制作が含まれる場合、これらのかけがえのない境界は他のプロジェクトに委ねられます。
  10. 境界をクリーンアップして書き留めます。秘密、備品、誇張された「パス」、再現不可能な英雄的な物語を残さないでください。

直感に反する最後の結論

本当に伝えられる AI エンジニアリングの話は、「22 時間連続で動作した」ということではなく、「AI は、いつ自ら動作するのか、いつ証拠を探すのか、いつスクリーンショットが信頼できないことを認めるのか、そしていつ人々にクリックさせるのかを知っていた」ということです。自動化の上限はクリック数ではなく、境界線の感覚によって決まります。

最良のエージェントがすべての決定を下してくれるわけではありません。本当に決定が必要な場合にのみ参加することができます。