開発mdx-playground

きっかけ:Markdown のままだとチャートが貼れない

beat-monitoring(決算ビート率モニタリング)は、もともと Markdown で銘柄ごとの表と簡単な解説を並べていた。BE.md を開き直して、ヘッダーに「Q1 売上ビート率 +3.2%」と数字を打ち込んだ瞬間、これは Vue ページじゃないと無理だと判断した。テーブルは作れても、棒グラフと右軸折れ線が刺せない。銘柄ごとの JSON を読ませて Chart.js でレンダーしたい。

朝のうちに memo/2026-05-18/beat-monitoring-vue-migration.md を立ち上げて、移行計画書を書き出した。BE をパイロットにして、いけそうなら16銘柄を一気に流す段取り。書き終えてから Codex(gpt-5.5)にレビューを投げた。

Codex に2回叩かれて方針が固まる

最初の Codex レビューで4点の致命的指摘が返ってきた。

  1. _redirects の Cloudflare 上限:銘柄ごとに /BE/beat-monitoring/BE にリダイレクトする案で、合計200行を超えそうだったがCloudflareは10000行まで許容するから問題ないと判明
  2. SSG プリレンダー漏れnuxt.config.tsnitro.prerender.routes に新しい一覧と詳細パスを追加していない
  3. 文字列→数値の変換:JSON の "$3.2B" を ChartJS にそのまま渡せない、純粋関数で剥がす必要がある
  4. Chart.js の SSR 問題<ClientOnly> で包まないとビルドが落ちる

計画書を直して再度 codex exec resume --last でレビューを回した。2回目はGreen。Codex の指摘で計画を直すと、実装中に踏むはずの地雷が前倒しで消える。手を動かす前に30分かけた価値はあった。

段階1:BE をパイロットで通す

BE.json を通期ガイダンスベースに更新するところから始めた。TripleBeatTable.vueBeatStockChart.vue を並列で実装させ、一覧ページ(銘柄リスト)と詳細ページ(チャート+テーブル)を同じターンで生やす。

parseCurrency という純粋関数を切り出して、"$3.2B"3200000000 の変換と単位サフィックス(B/M/K)の剥がしをカバー。テストを10件書いて全部 pass。generate-redirects.mjs に BE を1行足して、/BE/beat-monitoring/BE のリダイレクトも仕込んだ。

ローカルで pnpm dev を叩いて /beat-monitoring/BE を開くと、テーブルとチャートが揃って描画された。ここで一旦コミット。

Chrome DevTools MCP で画面を見たら表示が崩れていた

「いい感じに動いているはず」のつもりだったが、ユーザーから「Chrome DevTools MCP で見たら表示が崩れている」と指摘が飛んできた。慌ててデバッグ用 Chrome を立ち上げて MCP 経由で /beat-monitoring/BE を開いた。

スクリーンショットを撮ってピクセル数を測ると、チャートは描画されている(高さ98483ピクセルで縦長の SVG が出ている)。しかし解像度1700pxクラスのウィンドウでテーブルが画面幅いっぱいに広がりすぎて、カラムが間延びしていた。max-width で抑える方針を決めて、テーブルラッパーに max-width: 1200px; margin: 0 auto; を入れた。

「自分の画面では問題なし」と思い込まずに MCP で見に行ったから事故が一段で済んだ。

NVDA 用に売上・EPS・ガイダンスの3チャート

BE で型ができたので、次は本丸の NVDA に手を入れた。マイクロン銘柄の図解(過去に作ったやつ)を参考に、BeatExpectationsChart.vue を新規作成。売上・EPS・ガイダンスの3軸を棒グラフで並べ、各バーの上にビート率バッジを乗せる構成。

ユーザーから「過去4期だけじゃなくて8期、できれば12期入れて」と来た。最初は4期固定でハードコードしていた棒幅とフォントサイズを、quarters props で動的に計算するように書き直した。12期入ると棒が細くなるので、フォントサイズも quarters の値で Math.max(10, 14 - quarters / 2) みたいな式に置き換えた。

NVDA の FY24 のデータが手元になかったので、サブエージェントに「NVDA の FY24 各四半期の Non-GAAP EPS とコンセンサスを公式IR・SEC 10-Q から取ってきて JSON 構造で返して」と派遣して、その間にコード修正を進めた。

サブエージェントが返してきた JSON を NVDA.json にマージした直後、画面の株価が桁違いに小さく表示されているのに気づいた。÷100 していたが、NVDA の株式分割は 10:1 なので ÷10 が正解。サブエージェント側のミスではなく、計算ロジック側のミスだった。

テスト用4つ目チャートが本番化した

3チャート出した後、「もう1つテスト用にチャートを追加して。ガイダンス棒グラフの右軸に上振れ率を折れ線で乗せる」と指示が来た。気軽に削除できるように :test-right-axis="true" という props を切って、4つ目のチャートだけに右軸を出す実装にした。

レンダーされた4つ目を見た瞬間、「これいい」と判断が変わった。

「0をチャート中央に置いて、0以下を青で色分けして」

symmetric 軸(min=-max, max=max)に切り替えて、点と数値ラベルの色を >=0 ならマゼンタ系、<0 ならブルー系 で分けた。

「テストじゃなくて本番化、全チャートに右軸適用」

test-right-axis props を削除して、売上・EPS・ガイダンスの3チャート全部に右軸折れ線を恒久的に乗せた。テスト用に切り出しておいたから、削除作業は5分で終わった。

色の濃淡が薄すぎた

右軸折れ線を全チャートに適用した後、テーブルの凡例(ビート率の6段階:マゼンタ濃淡3段・ブルー濃淡3段)と折れ線の点・数値ラベル色を揃えたくなった。

「色の濃淡(テーブル凡例の6段階)を折れ線の点と数値ラベル色にも適用して」

cell-mag-1/2/3 / cell-blue-1/2/3 の背景色をそのまま点 fill と text color に流す実装にした。動かしてみると点の色は綺麗だが、数値ラベルが薄い背景色に引きずられて読みにくい。

「薄すぎる」

点の fill は背景色のまま(チャートの中で見ると点として認識できる)、テキスト色だけ別パレットに切り替えた。textPalette という別の配列を切って、薄い側を1段階濃くした。これで点はテーブル凡例と視覚的に一致し、数字は読める明度を確保できた。

16銘柄の EPS ガイダンス追加をサブエージェント14個に並列で振った

NVDA が片付いた段階で、残る15銘柄の JSON にも EPS ガイダンスを足す必要があった。手作業で 15 × 4期 × 2項目 を埋めると数時間溶ける。

add-guidance-eps-spec.md に作業仕様書を書き出した。各銘柄の Outlook セクションを公式IRから引いて、guidance.epsLow / epsHigh / epsConsensus / actualEps の構造で埋める、データソースのURL を source フィールドに入れる、までを定義。

別セッションを開いてサブエージェントを14個並列起動した。SMCI、MU、AVGO、TSM、AMD、INTC、QCOM、ARM、ASML、AMAT、LRCX、KLAC、LLY、その他で14銘柄。

数分後、14個のうち13個が完了。LLY だけ時間がかかっていた。

MU は Codex 版で差し替え、LLY は手動ラップで dev server を起動

13銘柄の結果を見比べた。MU の数値が、サブエージェント版と Codex(gpt-5.5)に並行で投げた版で食い違っている。Codex 版の方が四半期ごとのレンジが公式IRの Outlook 通りで、サブエージェント版は EPS のレンジを Non-GAAP と GAAP で取り違えている疑いがあった。

MU.json は Codex 版で全面差し替え。サブエージェント版は捨てた。

LLY は依然動いていたが、dev server を起動して全16銘柄が新構造で読み込まれるかを先に確認したかった。LLY の JSON に最低限のラッパー(guidance: { eps: null, source: null })を手で入れて、空の構造で型を通した。dev server を立て直すと、16銘柄すべてのページがエラーなくレンダーされた。LLY の中身は後でサブエージェントの結果をマージすればいい。

NVDA はそもそも EPS ガイダンスを公表していない

NVDA の guidance 欄が、4期とも epsLow: null で揃って表示されていた。データ取得側の漏れを疑って公式IRの Outlook セクションを見に行くと、Revenue / Non-GAAP Gross Margin / OpEx / Other I&E / Tax Rate の5項目のみで、EPS は明示しない慣例だと分かった。アナリストはこの5項目から自力で EPS を組み立てる。

NULL 表示のままだと「データ取得失敗」に見える。

「全部 NA 表示じゃなくて、表自体を非表示にしてコメントで『NVIDIAはEPSガイダンスを公表していません』と出して」

hasEpsGuidance という computed を TripleBeatTable.vue に足した。全期の epsLow が null なら表ごと描画せず、代わりに脚注ブロックを出す。NVDA だけが「ガイダンス表なし+脚注」になり、他15銘柄は通常の表で出る。

Twitter Grok にも数値検証を投げて、Codex 版を採用

念のため、MU・SMCI・AVGO の数値を Twitter の Grok に「公式IRの Outlook と一致しているか確認して」と投げた。Grok の返答とサブエージェント版・Codex 版を3並列で見比べると、Codex(gpt-5.5)の数値が最も公式IR の Outlook テキストと一致していた。

精度比較の結論:この種の数値抽出タスクは Codex (GPT-5.5) > サブエージェント (Sonnet) > Grok という順だった。今後の数値検証ワークフローのデフォルトを Codex に寄せると決めた。

最後に simplify でレビュー3エージェントを並列起動

実装が一段落した夜、simplify スキルでレビューエージェントを3つ並列に投げた。

  1. 品質レビューparseCurrency の境界値バグ(null 入力で NaN を返す箇所)
  2. 再利用レビューbeatBand ユーティリティが既存にあったのに、新しい色分けロジックを別関数で書いていた重複
  3. クリーンアップ:テスト用コメントの取り残し、BADGE_W という未使用定数、signColor のテンプレ展開漏れ

3エージェント並列で1ターンの中に指摘が揃って返ってきた。それぞれ別ファイルに反映して、コミットを分けた。Codex の事前レビューで地雷は減らせたが、実装後の自己レビューでもう一段詰めると、明日の自分が触りやすいコードになる。

引き継ぎ計画書を Codex に3回レビュー

明日以降の作業(LLY の中身マージ、12期データの追加取得、ガイダンス差異の分析)を memo/2026-05-18/beat-monitoring-handover.md に書き出した。Codex に投げて、致命的指摘が消えるまで3回回した。

1回目:「LLY の null ラッパーが本番に残るリスク」→ TODO に明示 2回目:「16銘柄のソース URL の鮮度をどう保つか」→ 月次で再取得する手順を追加 3回目:致命的指摘なし

振り返り

1日で巻いた中で一番大きいタスクになった。

  • Codex の事前レビュー2回で実装中の地雷を4つ消した
  • Chrome DevTools MCP で実画面確認してテーブルの幅崩れを発見、思い込みで進めずに済んだ
  • サブエージェント14個並列で15銘柄のデータ取得を並走、自分は NVDA のコード修正に集中できた
  • MU は Codex 版で差し替え、LLY は手動ラップで先に dev server を通した。完璧主義で止まらず、動くものを優先
  • NVDA の EPS ガイダンス非公表は公式IRを読みに行って初めて分かった。「データが取れない」ではなく「データが存在しない」と切り分けたことで、表ごと隠す UI に落ちた
  • simplify で3エージェント並列レビューを実装後に回した。Codex の事前レビューと役割が違って、実装後の重複・取り残しを拾うのに効く

判断と違和感を拾うのは自分、実行はサブエージェントと Codex と Chrome DevTools MCP、という分担で1日が回った。明日は LLY のデータマージから。