# codex レビュー結果 R3：学習と checkpoint の由来（2026-11-01 依頼 54）

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

**判定：必須1件、推奨2件です。** R2の5点は反映されていますが、全体タイムアウトで新設された停止処理には、子孫の取りこぼしと無関係なプロセスへの誤送信の可能性が残っています。

| 項目 | 判定 | 確認結果・修正案 |
|---|---|---|
| 時間切れ時の `kill_tree` | **必須** | [lab_train.sh:206](/Users/shimazueiichi/r2r2r-scripts/lab_train.sh:206) は、動いている子孫を一度列挙し、TERM→2秒後に同じPID一覧へKILLします。**走査後に生まれた子孫は取りこぼし、終了後に再利用されたPIDには無関係なプロセスでもKILLを送ります。** また、再帰的な走査自体には時間制限がありません。既存の [lab_lib.sh:80](/Users/shimazueiichi/r2r2r-scripts/lab_lib.sh:80) の停止方式に合わせ、生成を止めながら列挙し、プロセスの同一性を確認してから残存分を停止する構成にしてください。 |
| 子シェル化の回帰テスト | **推奨** | [test_lab_train.sh:410](/Users/shimazueiichi/r2r2r-scripts/test_lab_train.sh:410) は単純なHFハングを確認していますが、新しい停止処理の競合は押さえていません。「子孫を継続生成」「TERM中に子孫を生成」「由来処理中のINT・TERM」を追加してください。既存の見守り中TERMテストとは別経路です。 |
| 強制終了途中の由来ファイル | **推奨** | [provenance.py:307](/Users/shimazueiichi/r2r2r-scripts/provenance.py:307) は出力先へ直接書きます。KILLされると例外処理を通らず、不完全なJSONが残り、次回はO_EXCLでスキップされます。②③で再利用する前に、一時ファイルへ完成させてから上書きなしで公開する方式を検討してください。 |
| R2：全体の時間上限 | **据え置き・部分修正済み** | spec・Python・HF追加は子シェル全体の監視対象になり、HF内部も120秒固定です。ただし上記の停止処理があるため、終了・子孫停止までの保証は未完了です。 |
| R2：prepare失敗入力の固定 | **据え置き・修正済み** | [provenance.py:228](/Users/shimazueiichi/r2r2r-scripts/provenance.py:228) は `measured_at` の存在で再測定を防ぎ、指紋がnullの場合も開始時の失敗記録を保持します。configのSHA256も同様です。 |
| R2：旧STATEでの `--stop` | **据え置き・修正済み** | [lab_train.sh:170](/Users/shimazueiichi/r2r2r-scripts/lab_train.sh:170) は開始時情報を不明として記録します。停止時のコード・入力へ置き換わる経路は解消しています。 |
| R2：openpi保存・復元 | **据え置き・修正済み** | STATEへの保存と `--stop` での復元があります。旧STATEでは取得記録がない旨を残します。関連テストも追加されています。 |
| trap・cleanup・終了コード | **据え置き** | `finish` は先に `FINISHED=1` とし、終了コードをローカル変数に保持します。通常の子処理失敗でPod削除を再実行したり、保存した終了コードを変更したりする経路は見当たりません。Pod削除の試行は由来処理より先です。 |
| `RESULT_LINES` の受け渡し | **据え置き** | [lab_train.sh:229](/Users/shimazueiichi/r2r2r-scripts/lab_train.sh:229) で親が `PROVLINE` を読み、親自身の `RESULT_LINES` に追記しています。子シェル内の変数変更が消える問題はありません。 |
| 鍵・PodのIP／ポート | **据え置き** | 現在のspecにIP・ポートを直接渡す経路は見当たりません。鍵は引数・既知プレフィックス・secret-file内容・辞書キーの伏字対象です。任意のIPを除去する機能ではない点は従来どおりです。 |
| 既存KIOXIA・未マウント時 | **据え置き** | checkpointへの追記先は新規配置の最終照合成功後だけ設定され、既存1499には設定されません。由来処理はフォルダを作らず、O_EXCLで上書きを防ぎます。 |
| HF互換性・dirty対象 | **据え置き** | HF側・KIOXIA側の比較からルートの由来JSONを除外する処理は整合しています。`lab_start.sh` の利用経路にも破綻は見当たりません。指定4パスへのdirty限定も妥当です。 |
| `.DS_Store`・配置前の出力指紋 | **据え置き・仕様判断待ち** | 現行仕様どおりです。②③への展開時には、Finder操作と由来配置による再測定不一致を考慮した検証手順が必要です。除外規則を変える場合は指紋バージョンも更新してください。 |

読み取りレビューとシェル4ファイルの構文確認を行いました。テスト本体は一時ファイル作成・プロセス操作を伴うため実行しておらず、**28件（1 skip）・OK 177／NG 0は未追試**です。ファイル変更、実機・Pod・Hugging Faceの操作は行っていません。

## 反映（Claude Code）
- 必須（kill_tree）: 由来を書く子を set -m で自分のプロセスグループにして流し、時間切れのときはグループごと TERM、2 秒のうちに誰か残っていれば KILL（kill_group）。たどったあとに生まれた孫も取りこぼさず、番号の使い回しはグループの全員が終わったあとにしか起きない（KILL の前に pgrep -g で残りを見る）
- 推奨（テスト）: 偽物の hf_prov_hang を「TERM を無視して孫を次々に作る」に変え、打ち切って通知へ進み、孫（sleep 61）が残らないのを確かめる。由来を書く途中の INT・TERM は、wrap の中では trap '' で無視、early_fail の中では親の on_signal → finish（PROV_DONE=1 で2回目は書かない）となり、子は中の 120 秒の上限で終わる
- 推奨（書きかけ）: provenance.write を、同じフォルダの一時ファイルに書き終えてから link で置く形に（exFAT で link できなければ、ないのを確かめてから rename）。一時ファイルは残さない。test_provenance.py に link できない場合を足す（29 件・1 件 skip）
