決算書クイズに音声を入れた日
朝起きてすぐ、「クイズの問題文と選択肢と解説をずんだもんに読ませたい」と思いついて手を動かし始めた。ながら勉強できるようにすると、自分が一番ヘビーユーザーになる。
トレーニングしながら、料理しながら、画面を見ずに耳で受け取れる決算書クイズ。完成形が頭に浮かんでから、一気に走り出した。
Codexレビューを4回まわして計画を固めた
いきなり実装に飛び込みたくなる衝動を抑えて、まず計画書を書いた。書いては Codex に投げ、指摘を読んで書き直し、また投げる。これを4往復した。
最初の版では「トピックごとに LocalStorage キーを切る」と書いていた。Codex から「トピックを移動するたびに自動再生2倍の設定が初期化されて、ユーザーは毎回設定し直すことになる」と返ってきた。確かに、自分がユーザーだったら手で2倍をタップし直すのが面倒で離脱する。
2回目の指摘で「984問の音声を一括生成するスクリプトを、途中で落ちたら最初から作り直す設計になっている」と気づかされた。54トピック × 約18問の生成は数時間かかる。Pythonスクリプトに既存ファイルのスキップ処理を入れて、リトライで途中再開できる形に書き直した。
3回目で「再生中にトピック遷移したら音声が止まらない」、4回目で「audio要素のpreload設定がブラウザによって挙動が違う」を直して、ようやく計画書が落ち着いた。Codexが致命的な穴を見つけるたびに、自分の脳内シミュレーションの粗さを突きつけられた。
984問の音声を裏で焼きながら、UIを直す
Pythonスクリプトをバックグラウンドで走らせた。54トピック × 約18問 = 984ファイル。VOICEVOX ENGINE を叩いて wav を吐き出し、トピックスラッグごとのディレクトリに格納していく。
走らせている間にフロント側の音声コントロールバーを組んだ。OFF / 手動再生 / 自動再生、そして等倍 / 1.5倍 / 2倍。最初は fieldset と legend で意味的にラジオグループを作ろうとしたら、ブラウザのデフォルトスタイルで legend がブロック要素として上に飛び出し、コントロールバーが2段組みに割れた。
CSSで戦うのを諦めて、div と span に置き換えた。意味論より見た目を取った瞬間で、後ろめたさはあるが、ユーザーが触るのは見た目だ。
裏で焼いている音声生成のログを横目で見ながら、UI を1時間ほど詰めた。生成が止まっていないかを確認するためにログを30秒に1回眺める癖がついて、午前中はほぼスクリプトの番人だった。
LocalStorageキーをグローバル一本化した
Codex の1回目の指摘を受けて設計を変えた。
旧: quiz-audio-mode:{topicSlug} → トピックごとに設定がリセット
新: quiz-audio-mode → 全トピックで設定を共有
これだけのことだが、ユーザー体験は別物になる。一度「自動再生 / 2倍」を選んだら、次のトピックに移っても2倍で読み上げが続く。トレーニング中にトピックを切り替えるたびに画面を覗き込んで2倍をタップし直す手間が消えた。
実装は単純なキー変更だが、移行のためのコードを足した。旧キーが残っている場合は読み込んで新キーに転記し、旧キーを削除する。これでベータ版を試した数人のユーザーが、設定をリセットされずに済む。
ハイライト全件レビューの計画を立てる
音声と並行して、もう一つ走らせた計画がある。クイズのハイライト機能(解説中の特定単語に色をつける)が、54トピック × 約18問のすべてで正しく設定されているかを総点検する計画だ。
Codex に計画書を投げたら、致命的な指摘が2点返ってきた。
1点目は「単一の highlight キーで足りない問題がある」。たとえば Q4「会社関係者の総称」では、選択肢の解説で『株主』『債権者』『顧客』『従業員』のすべてを強調する必要があるのに、現状の設計だと highlight: 'investors' の1つしか指定できない。'all-stakeholders' のような複合キーを用意して、対応する単語群を一括で光らせる仕組みが要る。
2点目は「ハイライト対象語のリストとMDX本文の整合性チェックを自動化していない」。手で54トピック × 約18問を目視で照合するのは現実的じゃない。スクリプトで MDX をパースして、highlight キーに対応する単語が本文に存在するかを確認する仕組みを後段に追加する方針に切り替えた。
Phase 4〜7 を並列サブエージェントで生成
ハイライト書き換えとは別軸で、もう一つの仕事が残っていた。
- Phase 4: キャッシュフロー計算書編 6トピック
- Phase 5: 財務分析編 12トピック
- Phase 6: 開示・企業価値編 T45-T50
- Phase 7: workbook 業種当てクイズ・前期比較
これを順番に1人でやると数日かかる。Claude Code にサブエージェントを並列で立ててもらい、フェーズごとに分担させた。「Phase 4 と Phase 5 と Phase 6 を並列で生成して、終わったら全件 git status で漏れを確認して」と一言投げた。
サブエージェントが各フェーズの MDX を吐き出し、メインスレッドが最後に grep でハイライトキーの整合性を走査する。30トピック近くが午後の数時間で出揃った。自分は方針を一言渡したあと、出来上がった MDX を読んで違和感を拾う係に回った。
章番号と本質的なタイトルを書き換えた
Phase 7 まで終わって全体を眺めていたら、自分でも嫌な符合に気づいた。トピックの章番号とタイトルが、参考にした書籍の目次と酷似している。引用範囲を超えていないかが気になり、その日のうちに書き換えると決めた。
displayNumber プロパティを追加して、「1-1」「1-2」「2-1」形式の章番号を独自に振り直す。タイトルも、書籍の見出しに引っ張られた表現を一つひとつ書き直す。54ファイルすべてが対象だ。
これも並列サブエージェントに分担させた。フェーズごとに「displayNumber を振って、タイトルを本質的な問いに書き直して」と指示し、出力をメインで grep してチェックする。半日仕事を1〜2時間に圧縮できた。
夜、全54トピックを開いて目視で確認しながら、参考書籍を脇に置いて見比べた。タイトルが書籍の見出しと2文字以上連続している箇所をリストアップし、書き直しの2周目に入った。
ピンスケさんの図解をT46.5として追加した
夕方、X のブックマークCSV(前日エクスポートしておいたもの)を眺めていたら、ピンスケさんの投稿が目に留まった。PBR / PER / ROE / BPS / EPS の関係を1枚の図にまとめた投稿で、これを決算書クイズに取り込みたくなった。
開示・企業価値編のT45〜T50の間に「T46.5」として新トピックを差し込んだ。番号を T47 に繰り上げず .5 で逃げたのは、既存のリンクや LocalStorage キーが番号に紐づいているのを壊したくなかったため。displayNumber 側で「6-3」と表示し、ファイル名だけ T46.5 で残す。
引用元として投稿URLを記載し、図解の意図を自分の言葉で書き直した。1トピック追加するだけだが、決算書を読む視点が一段増えた。
今日の学び
Codexレビューを4往復したことが、今日一番効いた工程だった。LocalStorageキー設計、984問生成スクリプトの再開設計、再生中の遷移処理、ハイライトキーの複合化。どれも実装してから気づいていたら、半日以上溶かしていた。
並列サブエージェントは、自分が「方針を1行で渡せる」状態になってから本領を発揮する。今日は計画書を Codex で固めてから振ったので、戻ってくる成果物が方針通りに揃った。逆に方針が曖昧なまま並列化すると、回収のときに辻褄合わせで時間を食う。
そして、書籍の目次と似ているという小さな違和感を、その日のうちに54ファイルに着手して潰したのは正解だった。明日に持ち越したら、絶対に手が止まる。
明日やること
- ハイライト整合性チェッカーのスクリプトを書く(MDX本文に highlight キーの単語が存在するかを grep して、欠落をリスト化)
- 'all-stakeholders' のような複合 highlight キーの仕様を設計し、Q4を含む既知のケースを書き換える
- 984問の音声生成完了を確認し、ファイルサイズの合計と欠損ファイルの有無をチェック
- 章番号書き換えの2周目: 書籍の見出しと2文字以上連続している箇所をリスト化して書き直す
- T46.5(PBR/PER/ROE/BPS/EPS)の本文を読み返し、ピンスケさんの引用範囲が適切か確認する