name: device-verify description: 実機確認の標準ループ — 前提チェック (dev/release 判定) → reload/build 自動判定 → 反映 → take-ss.sh 撮影 → Claude Read 目視 → PR/レポート記録。修正・改善後の実機検証を毎回同じ手順で実施する。 user-invocable: true argument-hint: '[検証したい画面/機能の説明] (任意)'
/device-verify — 実機確認の標準ループ (Sess99 恒久策)
修正や改善を行ったら必ずテストと実機確認を行うための標準手順。 「JS だけならホットリロード / native・DB ならビルド」の判定 (Sess71) の先、 つまり「反映 → 撮影 → 目視 → 記録」までを毎回同じ順で実施する。
起動条件
- user が「実機確認して」「実機で見て」「スクショ撮って確認」「/device-verify」と言った時
- UI / 動線 / DB に触れる PR の merge 後 (PR テンプレ §6-3 が要求)
背景 (なぜこの順番か)
- Sess99 事故 1: 「DevBuild で進めている」前提で検証を始めたら、実機は Play 配信の release だった (installer=com.android.vending、DEBUGGABLE なし)。→ Step 1 で機械判定。
- Sess70 事故: JS-only 変更に 10-15 分のビルドを kick → Step 2 (Sess71 自動判定) で防止。
- Sess99 事故 2: DB migration はホットリロードでは適用されない (migration は app 起動時に 走る) → Step 3 に再起動 step を組み込み。
- R-1/R-25 教訓: 「✅ 生成完了」ログだけの報告で崩れを見逃す → Step 5 で SS を必ず Read。
Step 1: 前提チェック (read-only、毎回必須)
adb devices # 端末接続
adb shell dumpsys package com.dooooraku.bonsailog | grep -E "versionCode|installerPackageName"
adb shell dumpsys package com.dooooraku.bonsailog | grep -E "pkgFlags" # DEBUGGABLE の有無
curl -s -m 3 http://localhost:8081/status || echo METRO_NOT_RUNNING
判定表:
| 観測 | 意味 | 次の行動 |
|---|---|---|
installerPackageName=com.android.vending / DEBUGGABLE なし |
Play 配信 release が入っている | ホットリロード不可。dev build 差し替えが必要 → user に報告して承認を取る (アンインストール = 端末データ消去) |
| DEBUGGABLE あり | dev build | Step 2 へ |
| 端末なし | USB 未接続 | user に接続依頼 |
差し替え時の注意: 手元の古い dev APK は native 依存 (package.json の native module) が 最新 main と一致しているかを必ず確認 (古い APK + 新 JS bundle = native module 欠落 crash)。 不一致ならクラウドで作り直す:
gh workflow run build-android-dev.yml
RUN_ID=$(gh run list --workflow=build-android-dev.yml -L1 --json databaseId -q '.[0].databaseId')
gh run watch "$RUN_ID"
gh run download "$RUN_ID" -n bonsailog-dev-apk -D dist/
adb install -r dist/bonsailog-dev.apk
# 署名違いの release が入っている場合は事前に adb uninstall — データ消去 = user 承認必須
ローカル build は原則禁止 (WSL2 メモリ枯渇 → ページング I/O 失敗 → BSOD 3 回の前科。
scripts/guard-local-build.mjs がブロックする。lessons/build.md 参照)。
Step 2: 反映方法の判定 (Sess71 自動判定)
docs/how-to/development/dev-workflow.md の判定に従う:
- JS-only (
*.ts/*.tsx/ i18n / token) → Metro reload で数秒 - native 影響 (
package.json依存追加 /app.config.*/android|ios/**/plugins/**/patches/**) → cloud build (gh workflow run build-android-dev.yml、上記 Step 1 の手順) + install
Step 3: 反映
# Metro 起動 (未起動時、background)
corepack pnpm dev # PATH prepend 必須 (wsl2-environment.md)
adb reverse tcp:8081 tcp:8081
bash scripts/dev/reload-app.sh # wake → force-stop → start を 1 行で
- DB migration (schema v 上げ) を含む変更は、reload ではなく必ず force-stop → 再起動 (migration は起動時にしか走らない)。reload-app.sh は force-stop を含むためそのまま使える。
- Dev Client の「Continue」dialog は Back キーで dismiss (text tap 禁止、Developer Menu 誤起動)。
Step 4: 撮影 (take-ss.sh、CRLF 破損ガード付き)
bash scripts/dev/take-ss.sh <name> <session-tag> # dist/<tag>-verify-<ts>/SS-NN-<name>.png
- 検証対象の画面ごとに 1 枚以上。操作 (
adb shell input tap x y) →sleep 1-2→ 撮影。 - 座標は直前の SS を Read して決める (解像度は
adb shell wm size)。 - 日本語入力は adb 不可 (
bonsailog-adb-verify-constraints参照)、ASCII で代替。
Step 5: Claude Read 目視 (必須 — ログだけの合格判定禁止)
撮影した PNG を Read tool で開き、期待と一致するかを画面ごとに判定する:
- 変更点が画面に現れているか (文言 / レイアウト / カード / ダイアログ)
- 既存要素が壊れていないか (R-25 構造系: タブ構成 / セクション / UI 種別 / スクロール)
- dark mode 影響がある変更は light/dark 両方撮影
Step 6: 記録 + PR コメント SS 自動添付 (Sess108 #1293)
- 結果を PR コメント or セッション報告に SS パス付きで記載 (PR テンプレ §6-3 のチェックを更新)
- 完了報告には「やさしい説明」を必ず含める (PR テンプレ §2.5 と同義、Sess101 #1173)
- 不一致を見つけたら: 修正 → Step 2 から再実行 (合格するまで「済」にしない)
- 検証不能だった項目は「vc__ smoke に委譲」として明記 (黙って省略しない)
Step 6.1: PR コメントに SS path を自動投稿 (簡易版)
PR を持つ session では SS path のリストを gh pr comment で投稿する。 これだけでも
レビュアが code ブロックの path を Read で開けるようになり、 「SS なし PR」 を防げる:
# 撮影 SS をリスト化 (Step 4 の dist/<tag>-verify-<ts>/SS-*.png)
SS_DIR="dist/<tag>-verify-<ts>"
SS_LIST=$(ls -1 "$SS_DIR"/SS-*.png 2>/dev/null | sed 's|^|- |')
# 既存コメントを検索して重複投稿を回避 (冪等 — 同じ SS_DIR 文字列があれば skip)
PR_NUM=$(gh pr view --json number -q .number)
if gh pr view "$PR_NUM" --json comments -q '.comments[].body' | grep -qF "$SS_DIR"; then
echo "[device-verify] SS path コメントは既に投稿済 (skip)"
else
gh pr comment "$PR_NUM" --body "$(cat <<EOF
## 実機 SS (Step 6 自動添付)
撮影ディレクトリ: \`$SS_DIR\`
$SS_LIST
> SS は worktree 内の **絶対 path** で参照してください (\`Read\` tool で開けます)。
> GitHub Web UI で画像プレビューを見たい場合は Step 6.2 を参照。
EOF
)"
fi
Step 6.2: GitHub Web UI に画像を埋め込む (任意、 user 手動 step)
GitHub の Issue / PR コメントに画像をインライン表示するには 2 通りある:
- Web UI で drag-drop (推奨、 user 手動): ブラウザで該当 PR を開き、 コメント欄に
dist/<tag>-verify-<ts>/SS-*.pngをエクスプローラから drag-drop する。 GitHub がhttps://github.com/user-attachments/...に自動アップロード + Markdown 化する。 - gh API で base64 アップロード (CI 用、 上級):
gh api graphqlのuploadAssetmutation を使って画像を assets に push する手順あり (要gh auth refresh -s write:packages)。 現在のフローでは Step 6.1 の path 投稿で十分 (Read tool で見れる) なので、 user 手動 (1) を推奨する。
Step 6.3: stop-verify-gate.mjs 層 B との連携 (#1293 → #1319 証拠ベース化)
- Stop hook の 層 B (SS 不在ゲート) は #1319 で 証拠 (PNG 実在) ベースに変わった。
単語照合は撤廃され、
take-ss.shでdist/<tag>-verify-<ts>/SS-NN.pngが実際に生成 されているかをexistsSync+readdirSyncで確認する (「device-verifyと打つだけ」では 通過できない)。よって Step 4 (take-ss.sh で実際に撮影) を踏めば自動 PASS する。- 加点: 撮った PNG を Step 5 で Read 目視すると、その形跡が層 B の metrics に
ss_read=trueとして記録される (目視要件 Step 5 の近似)。
- 加点: 撮った PNG を Step 5 で Read 目視すると、その形跡が層 B の metrics に
- exempt の注意 (#1319 で厳格化): UI source (
src/app/plugins配下の.tsx/.ts等) を編集している場合は、commit subject prefix をchore:/refactor:等に しても exempt されない (= SS PNG を撮るまで block)。prefix exempt が効くのは UI source を一切編集していない harness / docs / 設定 (app.config.tsのみ) 変更に限られる。
つまずきと復旧 (Sess101 実走で実証した 3 件)
| 症状 | 原因 | 復旧 |
|---|---|---|
| adb コマンドが無応答 (timeout exit 124) | WSL2 adb daemon ロック (並行 adb / 残留 process、adb-daemon-parallel-hang 参照) |
/mnt/c/Windows/System32/taskkill.exe /F /IM adb.exe → adb start-server → adb devices で再認可確認 |
reload-app.sh が build を要求するが flag の native_files: (none) |
dist/.native-dirty の unknown_files にリポジトリ外 path (例: ~/.claude/settings.json = harness 設定) が混入した誤検知 |
flag の中身を Read で確認し、リポジトリ外 path のみなら rm dist/.native-dirty で続行 (構造修正 = Issue #1174) |
input tap が効かない / 別の要素に当たる |
Read で見る SS 画像は実画面の縮小プレビューで、見た目の座標と実 px がずれる | 座標は adb shell uiautomator dump の bounds が正 — bounds="[x1,y1][x2,y2]" の中心を tap する |
--from-ship 自走モード (ADR-0065)
/ship から --from-ship 付きで呼ばれた場合は終わり方が変わる:
- 単独起動時: 上記 Step 1-6 を実施し、結果を PR コメント / セッション報告に記録して終わる
(=
/shipを介さない通常の実機確認)。 --from-ship時:/shipの Phase 6 として呼ばれる。Step 1-6 を実施したあと、結果を/shipの Phase 7 (平易説明 + 計測 append) にそのまま引き渡す (= 「実機 OK でした」 で止まらず、 やさしい説明 + ⑦⑧ 計測まで自走する)。- 停止 3 例外との連携: Step 1 で release 上書き install が必要 (= 端末データ消去) なら、これは
停止 3 例外①不可逆操作 に該当するため、
--from-shipでも AskUserQuestion で承認を取る。それ以外 (reload/build 判定・SS 撮影・adb daemon ロック復旧) は停止理由ではなく自走で復旧する。 - 不一致を見つけたら
/shipの Phase 4 (実装) に差し戻して修正 → 再 verify (合格するまで「済」に しない)。これは計画変更ではないので止まらない (= 同じ AC を満たすための修正)。
関連
docs/how-to/development/dev-workflow.md(Sess71 reload/build 自動判定)scripts/dev/reload-app.sh/scripts/dev/take-ss.shdocs/how-to/testing/testing.md§9 (実機 adb 検証制約)- PR テンプレ §6-3 (実機確認の択一欄、Sess99 恒久策)
/verify(汎用 run-and-observe)、/release-check(リリース前照合)/ship(本 Skill を Phase 6 として内部駆動する司令塔、ADR-0065)