# コードレビュー依頼：学習と checkpoint に由来（provenance）を自動で記録する（2026-11-01 依頼 54・段階①）R3

前提: 学習済みの checkpoint に、どのデータ・どのコード・どの設定で学習したかの記録がない（既存 5 件はどれも `_CHECKPOINT_METADATA` の custom_metadata が空）。これからの lab_train.sh の学習で、作った時点に機械が読める形で残す。共通の部品 provenance.py は、このあと ②データセットを作るとき・③実機の試行でも使い回す。
過去のレビュー: docs/review_result_20261031_lab_train_codex*.md・docs/review_result_20261101_model_select_train_fixes_codex*.md

対象（`git diff HEAD -- lab_lib.sh lab_train.sh lab_train_fakes.sh test_lab_train.sh pi05/openpi_addon/hf_upload_ckpt.py pi05/test_hf_upload_ckpt.py` と、新しいファイル `provenance.py`・`test_provenance.py`。ほかのファイルの未コミット差分は対象外）

## 1. provenance.py（r2r2r-scripts 直下）
- `fingerprint(path)` → `files-v1:<sha256>`。ファイルを相対パス（NFC・/ 区切り）の UTF-8 バイト順に並べ、①全部の「相対パス\tバイト数\n」②名前が .json で 1MB 未満のものの「\0相対パス\0」＋中身、の順に sha256。画像・動画・重みの中身は読まない。`._*`・`.cache`・`.incoming_*` は数えない。リンクはたどらない
- `code_info(repo, paths)`: commit・dirty・dirty_files（git にないファイルも数える）・diff_sha256（`git diff HEAD --binary` と、git にないファイルの名前と中身の sha256）。paths を渡すとそのパスの中だけを見る
- `build(spec, data_root, ...)`: schema_version provenance/1・created_at（ISO8601・タイムゾーンつき）・producer（スクリプト・引数・機械）・code・external_code・inputs/outputs（path は r2r2r-data からの相対・指紋を測る）・config・compute・numbering・status（process と task を分ける）・error
- 鍵: 引数の --token / --key / --api-key などの値・hf_ / rpa_ などで始まる鍵らしい文字を `<redacted>`。--secret-file の中身が記録のどこかに出れば置き換える
- `write(rec, path)`: O_EXCL で書く（あれば上書きしない）。フォルダは作らない
- CLI: `fingerprint <path>` / `write --data-root --out ... [--code-path ...] [--secret-file ...] [--repo-dir] < spec.json`（終了コード 0 全部置いた / 3 もとからあって飛ばした / 1 失敗）

## 2. lab_train.sh
- 3f のあと、Pod の openpi の `git rev-parse HEAD` と `git status --porcelain`（addon・設定を入れたあと）を LAB_TO_SHORT の ssh で読む。読めなくても続け、由来に「読めなかった」と残す
- finish（成功・失敗・止めた・借りられなかった・--stop のどれでも通る）の頭で prov_write: lab/<Log>/train/train_provenance.json（あれば dest_of で別名）に置く。今回 KIOXIA に新しく置けたチェックポイント（place_ckpt が最後まで行ったときの PROV_CKPT_DIR）にも置く。もとからあった 1499 には置かない
- Hugging Face: 7b が `HFUP ok`（今回上げた）のときだけ、KIOXIA へ取ったあとで Mac の `hf upload` で train_provenance.json を足す（`HFUP same` のときは足さない）。hf_upload_ckpt.py の比べと lab_train.sh の compare_with_pod では train_provenance.json を数えない（次の回・取り直しで「別の中身」にならないように）
- status: completed（学習が最後まで）/ interrupted（signal・--stop・上限）/ failed（ほか）。task は not_applicable。片付けの一部が失敗したときは completed のまま error に書く
- compute: GPU_USED・Pod の id・作った時刻・学習を始めた時刻・消した時刻・rented_sec・COST_PER_HR・cost_usd・[COST] の行そのもの
- 由来を書く処理は lab_timeout 300 で包み、失敗しても終了コード・通知は変えない。KIOXIA がない（LOGDIR がない）ときは書かない（フォルダを作り直さない）

## 3. テスト
- test_provenance.py 23 件（指紋の作り方そのもの・数えないもの・JSON の中身・NFD・読めないフォルダ・鍵を伏せる・git の clean/dirty/paths・上書きしない・フォルダを作らない・CLI）
- test_lab_train.sh 17 節ほか（成功・落ちた・上限・借りられなかった・same・由来だけ上げられない・openpi が読めない・KIOXIA が外れた・dirty/clean・--stop・TERM・もとからある 1499）。全部で OK 166 / NG 0
- test_hf_upload_ckpt.py に「リポジトリに train_provenance.json があっても same」を足した

## 見てほしい点
1. 由来を書く処理が、学習の片付け（Pod を消す・KIOXIA に置く・通知）を止める・遅らせる・終了コードを変える道がないか（set -u・pipefail・trap の中・cleanup の中の wrap_fail）
2. 鍵（Runpod・Hugging Face）・Pod の IP とポートが由来・Hugging Face に入る道がないか
3. KIOXIA の既存のもの（checkpoints・datasets・runs）に書き足す・上書きする道がないか。KIOXIA が外れたときにフォルダを作り直さないか
4. 指紋の決め方が、②データセット・③実機の試行で使い回すときに困る点（.DS_Store を数える・出力の指紋を由来を置く前に測ることなど）
5. Hugging Face に train_provenance.json を足すことで、lab_start.sh（--model corr の hf download）・hf_upload_ckpt.py・compare_with_pod が壊れないか
6. git の dirty の見方（--code-path で lab_train.sh・lab_lib.sh・provenance.py・pi05 に絞る）が妥当か

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

## R3 で見てほしいこと
R1・R2 の結果と反映は docs/review_result_20261101_train_provenance_codex.md・docs/review_result_20261101_train_provenance_codex_r2.md の「反映」。R2 で反映した5点が直っているか、新しい問題（子のシェルで流すことによる trap・Ctrl-C・RESULT_LINES の受け渡し・kill_tree が自分以外を止める道）が出ていないか。テストは test_provenance.py 28 件（1 件 skip）・test_lab_train.sh OK 177 / NG 0。
