# コードレビュー依頼：学習を1行で回し、終わったらモデルを持ち帰り Pod を消す lab_train.sh（2026-10-31 依頼 49）

前提: 依頼 48（コミット c7ff7d9）の続き。今夜、エイイチが寝ている間に π0.5 の続きからの学習（`pi05_mecharm_trim_corr_lora`・`--init /workspace/ckpt/3999/params`・1,500 ステップ・exp `trim_corr_v3`）を回す。途中で人が何もしなくても、最後に Pod が消えている形にする。
過去のレビュー: docs/review_result_20261031_lab_start_model_check_codex*.md（pkill が自分の bash -c に当たる件）

対象（新しいファイルと `git diff HEAD -- lab_lib.sh lab_start.sh`。ほかのファイルの未コミット差分は対象外）:
- `lab_train.sh`（新）: 本体。中身は main に入れて最後に呼ぶ
- `lab_lib.sh`: lab_start.sh 3b〜3f のコマンドを関数に移した（lab_start.sh と共用）。lab_train.sh 用に Pod の中で流すコマンド（学習の様子・始める・止める・一覧・見張り）、Mac 側（一覧・hf download・通知・caffeinate）、費用の行を足した
- `lab_start.sh`: 3b〜3f を lab_lib.sh の関数に置き換えただけ（流すコマンドの中身は同じ）
- `pi05/openpi_addon/hf_upload_ckpt.py`（新）: Pod で最後のチェックポイントを非公開リポジトリへ上げ、一覧を確かめる
- `lab_train_fakes.sh`（新）: `--dry-run` の差し替え（API・SSH・hf・通知）
- テスト: `test_lab_train.sh`（新 105 件）、`pi05/test_hf_upload_ckpt.py`（新 8 件）、`test_lab_start.sh`（既存 93 件。そのまま通る）

## 流れ
1. Pod を作る（名前 `p2m-train-<Log>`、記録 `lab/<Log>/train/lab_pod.json`。lab_start.sh の `p2m-<Log>`・`pod/` とは別）。A40 を 30 秒おき 5 回、空きがなければ RTX A6000・L40S（48GB）を 2 回ずつ。`--no-fallback` で A40 だけ
2. SSH を待ち、Pod 側に見張り（`lab_watchdog.sh`）を setsid nohup で置く。上限（Pod を作った時刻 + `--limit-hours` 既定 5 時間）+ 60 分を過ぎたら、Runpod が Pod に入れる `RUNPOD_POD_ID`・`RUNPOD_API_KEY`（Pod 用の鍵）で REST の DELETE → だめなら runpodctl remove → stop。Mac の鍵は送らない
3. lab_start.sh 3a〜3f と同じ準備（サーバーは起動しない）→ tar を送る → `pod_prep_trim_corr.sh 1500 /workspace/ckpt/3999/params`
4. `train_run.sh` を Pod に置いて setsid nohup で回す（train.py の終了コードを `/workspace/logs/train_exit_code` に書く）。nvidia-smi を 30 秒おきに `gpu_train.csv`
5. 5 分おきに様子（`STATUS run= exit= age= bar= logstep= loss= ckpt= cm=`）を見る。終了コード 0 かつ最後のチェックポイントが `steps-1` で `_CHECKPOINT_METADATA` あり → 成功。終了コードが 0 以外・印なしでプロセスが消えた・ログが 30 分書かれない・上限を超えた・Pod が RUNNING でない → 失敗
6. 成功: 見張りを 3 時間延ばす → Pod 側の一覧（train_state なし）→ hf_upload_ckpt.py（非公開で作る・非公開でなければ止まる・別の中身があれば止まる・上げたあと一覧を比べる）→ ログを持ち帰る → Pod を消す → Mac の hf download で KIOXIA の `checkpoints/pi05_mecharm_trim_corr_lora_v1/.incoming_*` に取り、Pod 側の一覧と名前・バイト数を比べてから `<step>/` に mv。`<step>/` がもとからあれば触らず比べるだけ
   上げられなかったとき: Pod を消す前に scp で KIOXIA へ取る（同じ比べ方）
7. 失敗: 学習を止め、ログを持ち帰ってから消す。Ctrl-C・TERM・HUP も同じ。set -u などで思わぬところで終わっても EXIT の trap で同じ片付けをする
8. 通知（osascript）と `result_<時刻>.txt`。Pod を消せなければ「残っているかもしれない」と `--stop` を案内
9. `--stop`: 記録の Pod の学習を止め、ログを持ち帰って消す

## 見てほしい点
1. Pod が残る道がないか（どの段階で落ちても・Ctrl-C でも・Mac が止まっても）。逆に、学習が正しく終わっているのに失敗扱いで消す道がないか（成功の判定・止まりの判定 30 分・tqdm の行の読み方）
2. pgrep / pkill が自分の bash -c に当たらないか（`^bash /workspace/r2r2r/train_run\.sh$` などの ^ を付けた形にした）。setsid nohup で ssh が返るか
3. Pod 側の見張り：Pod 用の鍵の探し方（環境変数・/proc/1/environ・/etc/rp_environment）、鍵をログ・画面に出さないか、時刻を延ばす仕組み、上げる途中で消えないか
4. Hugging Face：非公開の確かめ・上書きしない・train_state を上げない・一覧の比べ方（hf_upload_ckpt.py と Mac の lab_list_files）。Mac の hf download に鍵を HF_TOKEN_PATH（ファイルの場所）で渡すこと
5. KIOXIA：もとからある checkpoints/・datasets/・runs/ を上書き・移動・削除しないか（rm -rf は自分で作った一時フォルダの中だけか）
6. lab_start.sh の今の動きが変わっていないか（3b〜3f のコマンドの中身）。lab_start.sh の「同じ Log の Pod」判定とぶつからないか
7. テストが 4 つの場合（終わる・落ちる・上限を超える・Hugging Face に上げられない）とそれ以外を十分に確かめているか

ファイル変更・実機操作・Pod の操作・Hugging Face の操作はしないでください。読み取りだけでお願いします。日本語で、表（項目 / 判定 / 確認結果・修正案）と、必須 / 推奨 / 据え置きの分類で返してください。
