R2のTTS音声WAV 4,127件を安全に消す:Codexレビュー必須の削除スクリプトとDropbox退避、配布Excelの命名ルール

開発eurekapu-nuxt4

R2のTTS音声WAVを安全に消し、配布Excelの命名をそろえる

R2 に溜まった仮ナレーションの WAV を消す作業は、前日の時点で削除の直前まで来ていた。 今朝は、その続きから始めた。

まず、MP3 の元になった WAV と R2 の WAV を突き合わせた(Step 1)。 R2 の MP3 3,647 本をすべて照合して、説明のつかない食い違いは0件だった。 消す準備は整った。

削除スクリプトは誰がレビューしたのか

実行の直前に聞いた。 この削除スクリプトって、Codex のレビューを受けたんでしたっけ。

受けていなかった。 Codex が見ていたのは「何を消すか」のほうで、消す一覧の 4,127 件、照合結果、本番が壊れないかを確かめて「削除してよい」と判定していた。 消す処理を書いた delete-wav.py そのものは、レビューを通っていなかった。

削除のね、スクリプトむちゃくちゃ怖いんですよ。

R2 にはゴミ箱がなく、消したら戻らない。 今のスクリプトは筆者に実行してもらう前提で急いで作ったもので、守りが薄い、という説明だった。 そこで、安全版の削除スクリプトと復元スクリプトを Claude Code に作り直させた。

安全版は、表示だけのモードで全 4,127 件を検査して通った(控えの MD5 を全件計算し直して一致)。 そのうえで Codex のレビューに出した。 1回目の指摘を直し、2回目で「このまま試し削除に進んでよい」が出た。 最後に出た low の指摘も直してある。

10件で試してから、4,127件へ

試し削除は、筆者が自分で打った。

python -I delete-wav-safe.py --prefix <対象フォルダ> --limit 10 --execute --confirm 10

10本とも成功し、対象以外の変化は0件だった。 本番の Git 講座で音声が鳴るかは、Playwright で確かめてもらった(筆者の Chrome は DevTools の接続口が閉じていたため)。 消す、本番で再生を確かめる、戻す、までを一通り試し、どれも問題なかった。

残りの全件も、筆者が実行した。

python -I delete-wav-safe.py --execute --confirm 4127 --jobs 8

画面には何も出てこない。 Python が出力を最後まで溜めていたためで、進み具合はログで見えていた。 削除は 07:20 に始まり、07:26 の時点で 291 件を消して失敗は0件だった。

それでも気になって、もう一度聞いた。

変なやつ削除していってないですか?大丈夫ですか?

実行中のまま R2 の全件を取り直し、ログと突き合わせてもらった。 変なものは消えていなかった。

続けて、当たっていなかった修正(ログへの排他)を直させた。 全件削除は、そのあと途中で止まった。 止まった時点の R2 を全件照合すると、消えていたのは削除一覧に入っていたものだけで、残りは 1,882 件だった。

再実行は PowerShell から筆者が流した。 08:57 の時点で 48 件を消し、失敗は0件だった。 50 件ごとに件数が画面に出て、終わる見込みは 09:40 ごろだった。

終わったあと、R2 を全件取り直して確かめた(Step 3)。 目標は WAV 388 本、MP3 3,647 本、約 3.1 GiB で、R2 の状態は期待どおりだった。 本番の画面が要求する MP3 を引くと 404 が5件出たが、いずれも今回消したフォルダの外のものだった。 R2 の仮ナレーションの WAV は0本になった。 消したのは削除一覧の 4,127 本だけで、本番の再生も問題なかった。

Phase 5:R2を書き換える前にDriveとDropboxへ逃がす

次は、Notion バックアップ用のバケットと動画のバケットを片付ける段階に入った。 消す前に、Google Drive と Dropbox の両方へ退避する。

Google Drive へは gws CLI で上げた。 Windows の .CMD ランチャー経由だと JSON の引数が壊れたので、Node から本体の run-gws.js を直接呼ぶ形に直させた。 Drive 側は Notion バックアップの 75 件で MD5 が一致し、最終的に 254 件すべて一致した(足りない、中身違い、重複、余分のいずれも0)。

動画は 720p 版を作った(535 MB、1280×720、1,777 秒で元と同じ長さ)。 動画の配信は Cloudflare にキャッシュされていなかった(cf-cache-status: DYNAMIC)。 そこで 720p 版は今と同じ場所に上書きする方針にした。 区切りのファイル数も元と同じ 178 本なので、古いファイルは残らない。

Dropbox へは rclone で入れた。 最初は接続設定のコマンドが動かなかった。

 ! rclone config create dropbox dropbox

入力欄の先頭に空白があったため、コマンドとして実行されず、文として Claude Code に届いていた。 1文字目に ! を置いて打ち直すと、Dropbox の接続ができた。 ただ、その出力でアクセストークンと更新用トークンが会話ログに表示された。 照合が終わったら接続を解除するよう勧められた。

置き場所は Dropbox 直下の退避用フォルダにした。 10:44 の時点でサーバーに届いたのは 254 件中 195 件で、届いた分の content_hash はすべて一致していた。 最終的に Dropbox も 254 件すべて一致し、保存場所は計画書の Phase 5 に表で残させた。

R2 を書き換えるスクリプト phase5-r2-ops.py には、ここでも Codex のレビューを条件にした。

私が実行するのはいいんですけど、codexのレビューを必ず受けてください。

重点は2つあった。 手元のドライブ(C: など)に書き込んだり消したりしないこと、R2 の対象の2か所以外に触れないこと。 レビューは3回回り、3回目で「このまま実行してよい」が出た。

操作の①から④は、筆者が PowerShell で実行した。 ①で Notion バックアップのバケットは空になった(75 件削除、失敗0、対象外の変化0)。 ②と③も済ませ、④で元の 1080p 動画の区切りファイル 178 件(1.97 GiB)を消した。 R2 全体は 9.77 GB になり、無料枠の 10 GB を切った。

Phase 6と後片付け

Phase 6 では、WAV を R2 に上げない運用を固めた。 変換とアップロードのスクリプトは、すでに voiceover を受け付けずに止まるようになっていた。 これをテストで固定し、置き場所を定数にした。 テストは 19 件すべて通り、運用のルールは CLAUDE.md に書かせた。

最後に、筆者が rclone config delete dropbox を打った。 rclone に残る接続先は R2 だけになった。 Dropbox の接続済みアプリに残る rclone は、ブラウザ操作で Claude Code に外してもらった。 計画書に残っていた未了の7件も片付けて、計画書を「完了」にした。

配布Excelは、どれが新しいのかわからなくなっていた

並行して、IB フォーマット統一の続きを進めていた。 引き継ぎで残った小さな後片付けの途中で、Dropbox にある3本について、R2 の控えとどちらが新しいかを比べさせた。 xlsx の中には Excel が保存した日時が残っているので、それと中身を比べた。 結果は予想と逆で、3本とも R2 の方が新しく、Dropbox が古いまま残っていた。

作った日付、年月日、日時、あと時間をなんでファイルに入れてくれないんですかね。

まさに講座を作っている最中に、版が追えなくなっていた。 しかも、ファイルの中の日時は当てにならなかった。 減算の演習の R2 版は 10-06 に直した版なのに、中の日時は 2024-09-25 のままだった。 スクリプトで直したときに日時を更新していなかったためである。

ルールにしてませんでしたっけ、と聞くと、ルールはあった。 ただし CF精算表の範囲に限った書き方で、Excel 講座の演習ファイルには当てていなかった。 スクリプト側に入っていなかった理由は、2つあった。 日時付きで保存するコードが共通の部品になっていなかったことと、新しい生成器を書いたときに CF 側の実装を見に行かなかったことだった。

名前の決め方は、二度変えた。 最初の提案は、公開用の固定名(_Final)と日時付きの保管庫の2本立てだった。 ただ、固定名に上書きしていくと、どの日時の版が確定したのかが結局わからなくなる。 そこで日付の後ろに _Final を付ける形にした。 最終的には _Final もやめて、_提出版 にした(例:EXCEL_…_20261015_064000_提出版.xlsx)。 配信先や Web に出して、受講者が使える状態にした版、という意味である。 修正が入れば新しい日時の _提出版 ができていくので、確定かどうかを決めなくて済む。

日時付きで保存する部品を作り、ルールは excel-generation.md に書かせた。 卒業課題の解答(10-10 版)にも IB の色を当てた。 相対パスを Excel が解決できずに止まったので、絶対パスで再実行させた。 992 セルの色を変え、監査で IB と食い違うセルは0になった。 式と値と計算結果は元と同じだった。

V2という名前をやめて「新版」「旧版」に

ファイル名の V2 も気になった。 日付で版を管理しているのに、バージョン1、2、3と付くのは意味がない。 調べてもらうと、v2 の付いた演習ファイルは10本あり、そのうち7本は本当に作り直した2版目の印だった。

名前は日本語の「旧版」「新版」にした。 講座のタイトルだということが、これで伝わる。 ファイルの中には v1、v2 の文字が無かったので、変えるのはファイル名だけで済んだ。 届けるときに名前を変える機能をスクリプトに足し、対象12本の dry-run を経て、11本中11本を Dropbox へ届けさせた。 値のみの演習の1本は、あるべき版を確かめるまで保留にした。 調べると R2 の版が正しく、10-04 のフィードバックで作り直した版だった。

改名の準備の途中で、穴も見つかった。 Dropbox にある INDEX-MATCH関数_03/04/05 と VLOOKUP_05 は1版目(講座では使われていない古い版)だけで、いま使っている2版目は R2 と生成フォルダにしかなかった(ハッシュで確認)。 講座の本文と図からも「バージョン1、2」の言い方を外し、古い版と新しい版に統一した。 画面再現のウィンドウ名に残っていた _v2_ は4場面で直した。 このとき、perl の置き換えが当たらなかった。 perl がスクリプト中の日本語を UTF-8 として読んでいなかったためで、-Mutf8 を付けてやり直させた。

コミットは Codex のコミットゲートで5回止まった。 旧版の扱い、途中で失敗したときの復旧、R2 の上書き防止などの指摘を、そのたびに直した。 直さなかったのは1件だけである。

外部ブック参照をどう切るか

ゴールシークの演習は、別のブックを参照していた。 参照先のファイルは元の場所にもう無く、後の版だけが残っていた。 切り方は、筆者が決めた。

配布済みのExcelの他のブックのシートを参照するのであれば、当該シートに新規追加して、値貼り付けして、書式だけ貼り付けたパターンで、どのファイルかっていうのを示すこと。

そうしないなら、計算式をすべてそのシートの中で完結させて、普通にシートとして追加する。 この2つのやり方で切らせた。

ここでも途中で何度かつまずいた。 Excel にスラッシュ区切りのパスを渡してファイルを開けず、Windows 形式に直させた。 連結精算表では、コマンドラインから渡した日本語のパスが化けたので、引数を書いた BOM 付きの呼び出し用ファイルを経由させた。 外部参照の先も、TB の2シートだけではなかった。 AJE_01 への参照が 3,560 セルあり、これも取り込みに加えた。

直す前と後で元からあるシートの計算結果を比べ、全セル一致を確かめた。 最後まで残った1冊は、筆者が推奨案を選んで直させた。 外部リンクの控えに残っていた当時の値から、取り込みシートを作るやり方である。 計算結果は 994 セルすべて一致し、動的配列の数式 27 個もそのまま残った。 配布用フォルダの 139 冊すべてで、外部リンクは0になった。

引き継ぎ

どちらの作業も、区切りで計画書の進捗を更新させ、次のセッション向けの引き継ぎをテキストファイルに書き出させた。 IB フォーマットの最後のコミットは push 済みで、残るコミットはないことを確かめた。 計画書を作らずに始めていた色そろえのセッションには、あとから進捗記録を作らせて「次セッションでやること」を6項目足した。

学びメモ

  • 削除は「何を消すか」と「どう消すか」を別々にレビューする。消す一覧が Codex の判定を通っていても、消す処理そのものはレビューを通っていなかった
  • ファイルの中の日時は、版の手がかりにならない。名前に日時を入れ、使える状態にした版は _提出版 の名前で示す

R2 の WAV の計画書は「完了」になった。 残っているのは、任意の次の削減候補の検討だけである。

#Cloudflare R2#rclone#Codex#Excel#命名規則#IBフォーマット