# codex レビュー結果：lab_start.sh のつなぎ直し（2026-10-31 依頼 44）

依頼文: docs/review_request_20261031_lab_start_reattach.md（codex exec -s read-only）

判定は **必須修正あり** です。通常の再接続経路は妥当ですが、API 応答異常と同時起動に対して、二重作成を防ぎ切れていません。

読み取りのみで確認しました。書き込みを伴うテストは実行せず、47 件 OK は未追試です。

| 項目 | 判定 | 確認結果・修正案 |
|---|---|---|
| 一覧 JSON の検証 | **必須** | [lab_start.sh:166](/Users/shimazueiichi/r2r2r-scripts/lab_start.sh:166) の `same=$(jq …)` は終了コードを確認していません。HTTP 200 でも不正 JSON・`null`・想定外の構造なら、同名なしとして POST まで進めます。最初に「配列で、各要素が必要な属性を持つ」ことを検証し、解析失敗は必ず停止してください。 |
| 同じ Log の同時起動 | **必須** | [lab_start.sh:133](/Users/shimazueiichi/r2r2r-scripts/lab_start.sh:133) から状態保存までロックがありません。2 プロセスがともに「記録なし・一覧に同名なし」を通過すると、両方 POST します。Log 単位の排他を、状態読み込み前から保持してください。Mac の bash 3.2 なら原子的な `mkdir` によるロックなどが候補です。 |
| stopped の削除確認 | **必須** | [lab_start.sh:147](/Users/shimazueiichi/r2r2r-scripts/lab_start.sh:147) の子プロセス失敗時に停止する点は正しいです。ただし、呼び先の [lab_stop.sh:127](/Users/shimazueiichi/r2r2r-scripts/lab_stop.sh:127) は `! jq -e …` により **解析エラーまで消滅確認成功にします**。変更対象外の既存問題ですが、今回これを自動削除の根拠に使います。呼び先の検証を直すか、start 側で厳密な一覧検証と消滅確認を追加してください。 |
| モデルの完了判定 | **必須** | [lab_lib.sh:124](/Users/shimazueiichi/r2r2r-scripts/lab_lib.sh:124) は `assets/local` だけ存在すれば、重みがなくても `ckpt=1` です。ダウンロード中断がファイル取得の合間なら `.incomplete` がない場合もあります。成功した download 後の完了マーカーと、必要な重み・対象 assets の確認を組み合わせてください。`find` の失敗も現在は欠損なし扱いになります。 |
| GET /pods/ID の 404 | **据え置き** | 公式 OpenAPI は 404 を `Pod not found` と定義しています。一覧でも不存在を確認する方針は妥当です。ただし一覧の型検証は必要です。[Runpod OpenAPI](https://rest.runpod.io/v1/openapi.json) |
| RUNNING 以外の扱い | **推奨** | [lab_lib.sh:107](/Users/shimazueiichi/r2r2r-scripts/lab_lib.sh:107) は未知の値まで stopped にします。公式 v1 の列挙は `RUNNING / EXITED / TERMINATED` なので、削除対象は **EXITED のみ**にし、未知値は停止する方が意図に合います。[Runpod OpenAPI](https://rest.runpod.io/v1/openapi.json) |
| 起動中 Pod の誤削除 | **据え置き** | `desiredStatus` は実際の起動完了ではなく要求状態です。イメージ取得中も RUNNING なので、通常の起動待ちを EXITED と誤認する構造ではありません。SSH 待ちへ進む実装は妥当です。[Runpod 公式 CLI 説明](https://github.com/runpod/runpodctl/blob/main/README.md) |
| 200・id なし／非 2xx | **推奨** | [lab_lib.sh:103](/Users/shimazueiichi/r2r2r-scripts/lab_lib.sh:103) は `{}` や `[]` も gone にします。後段の同名確認はありますが、これらは仕様上の Pod 応答ではありません。200 の構造不正は停止、id は要求 id との一致も確認。401・403・5xx も原則停止とし、404 のみ不存在確認へ進めると明確です。 |
| pgrep と LISTEN | **据え置き／推奨** | `[s]cripts/…` による自己一致回避と、local address の `:1F40`・状態 `0A` の読み方は妥当です。ただし別プロセスの 8000 LISTEN と serve プロセスを組み合わせても成功します。可能ならサーバーへの応答確認を加えてください。 |
| serve=1・listen=0 | **推奨** | 即再起動は、初回起動がモデル読込中の場合にも発生します。短い再確認期間を設けてから再起動してください。また `tmux kill-session -t serve` で止められるのはそのセッション内のプロセスであり、外で起動した serve は残り得ます。 |
| 3a の毎回転送 | **据え置き** | 再接続でも最新スクリプトを転送する方針は妥当です。ただし SCP は削除済みファイルを消さないため、完全な同期ではありません。 |
| 3f の再実行 | **据え置き・確認留保** | 再起動する経路だけで実行する構成は妥当です。`install_addon.py` の二重登録回避は確認しました。SSD 上の `add_trim_config.py` の冪等性は今回確認していません。 |
| 子プロセス・tee・トンネル | **推奨** | 子の `exec > >(tee …)` は親の FD を変更せず、ログを双方に残せます。通常の DELETE 失敗も親へ伝わります。一方、トンネル終了は全 Log 共通のパターン検索なので、別 Log の使用中トンネルも閉じます。自動呼び出しでは対象 Pod のトンネルだけに限定するのが望ましいです。 |
| HTTP の表示 | **推奨** | `RUNPOD_HTTP=""` は unbound variable を防ぎますが、`$(runpod_api …)` 内の HTTP は親に戻りません。後続エラーで空欄や以前の 200 を表示し得ます。HTTP を表示する呼び出しも、一時ファイル方式などに揃えてください。 |

テスト追加の優先順位は次のとおりです。

| 項目 | 判定 | 確認結果・修正案 |
|---|---|---|
| API 異常系 | **必須** | 一覧の不正 JSON・`null`・`{}`・空本文、一覧取得失敗を追加し、POST・DELETE がないことを確認。 |
| 停止失敗 | **必須** | DELETE 失敗、削除後も一覧に残る、削除確認の JSON 異常を追加。子の非 0 終了後に archive・POST しないことを確認。 |
| 同時起動 | **必須** | 同じ Log の2プロセスを一覧確認地点で同期させ、POST が最大1回になることを確認。 |
| 不完全なモデル | **必須** | assets だけ、重み欠損、完了マーカーなし、探索失敗を追加。現在は「assets だけで成功」を正解としてテストしています。 |
| probe と再起動 | **推奨** | serve=0/listen=1、起動途中、別プロセスの LISTEN、SSH 失敗、前ログ取得失敗、tmux 起動失敗を追加。 |

特に、**一覧の解析失敗を「不存在」に変換しないこと**と、**Log 単位の排他**を先に直すのがよいです。
