開発eurekapu-nuxt4

Nuxt4移行プロジェクトでコンテンツのHTML化ルールを策定した話

朝、handoffドキュメントを開いた瞬間に積み残しの輪郭が見えてきた。前日のPR #28はまだマージ前で、そこから本番デプロイと converter改修と manifest化が連なっている。「全部やって」と一言投げて、後は判断する側に回った。

午前のセッションが落ち着いたところで、ふとコンテンツ配置に違和感が残った。同じ「純粋な解説テキスト」の章なのに、ある章はVueに置かれていて、別の章はHTMLに切り出されている。バンドルサイズの数字は安全圏に入ったのに、運用ルールが空白のままだった。「中途半端」という言葉が口から漏れた。

午後はその違和感を埋めに行った。ルールを言語化してCLAUDE.mdに書き込み、次セッションへの引き継ぎプロンプトを残した。翌セッションでPhase F-1の実行を試したら、想定外の理由で中止判定が出た。

バンドル削減は終わったのに、運用ルールが残っていた

午前のセッションは、handoffを開いた時点でやることが並んでいた。

  • PR #28 のレビュー → squash マージ
  • 本番デプロイ(Worker 2.744 MiB / margin 261.7 KiB)
  • converterに「変換前の Vue構文検出」を追加
  • manifest化(CHAPTERS に available: true を追加して import.meta.glob を削除)
  • 簿記3級ノートのslipsパイロット(CSS namespace化 + HTML化)
  • 残り5件を一括HTML化してPR #29 を作ってマージ

PR #29 がマージされた時点で、Worker bundle は 2.670 MiB gzip / margin 337.8 KiB に着地した。Plan Cで予測していた 30〜50 KiB削減を超える -75.7 KiB が出て、marginは Green の領域に押し戻された。

ここで一度ユーザー側が手を止めた。「ぶっちゃけ今のままでもいいんですかね。なんか全体的に中途半端な気がしてて」。

数字の問題は片付いていた。それでも違和感が残っていたのは、ルールが言語化されていなかったからだった。同じ純粋テキスト章でも、PR #29で触った6件はHTMLになり、24件はまだVueに残っている。新しく章を追加するときにどっちを選ぶか、誰も決めていない。

判断軸を言語化する

整理してみると、判断軸は2つしかなかった。

  • インタラクティブ教材は Vue — 仕訳エンジン、クイズ、状態を持つUI。コーディングしやすさが上回る
  • 純粋な解説テキストは HTML — 静的なテキストと表だけの章。Vueに置くとレンダリングコードがバンドルに乗ってエンドユーザー側で無駄が出る

Vueファイルにテンプレートを書くと、たとえ中身が <p><table> だけでも、コンパイル後はレンダリング関数としてWorkerバンドルに積まれていく。静的HTMLで配信すれば、Workerは単にファイルを返すだけで済む。エンドユーザーが解説テキストを読むだけのために、Vueのレンダリング層を経由させる必要はなかった。

ルールが決まった瞬間、過去24件のHTML化が「ルールに揃える後追い」として意味を持ち始めた。ルールがないまま放置すると、また別の章が新しくVueに増えて、永遠に中途半端のままになる。

Vue残置 vs HTML化候補をテーブルに固定する

判断軸が決まったところで、過去のコンテンツを棚卸しした。Explore agentを5並列で派遣して、各カテゴリの中身を分類してもらった。

カテゴリVue残置(インタラクティブ)HTML化候補(純粋テキスト)
簿記3級ノート仕訳エンジン搭載章解説テキスト中心の章(PR #29 で6件着地)
法人税申告書別表計算フォーム付き解説章
宅建過去問インタラクティブ解説章(49件 HTML化済み)
財務諸表(該当少)解説章 2件(Phase F-1候補)
Excel解説動的サンプル純粋チュートリアル

このテーブルを Plan E(マスタープラン)の中で固定して、Phase F-1〜F-4に分けた。F-1 は財務諸表の2件、サイズが最小なのでパイロットとして置いた。

CLAUDE.mdとmemoに足跡を残す

ルールはCLAUDE.mdに書き込んだ。ただしルール本文だけ書くと、後から読み返したときに「なぜこう決めたか」が消えてしまう。今回の議論ドキュメントをmemo配下に残して、CLAUDE.mdからは参照を貼る形にした。

## コンテンツの配置ルール(Vue vs HTML)

- インタラクティブ教材(仕訳エンジン・クイズ・状態を持つUI)→ Vue
- 純粋な解説テキスト(静的な文章と表だけ)→ 外部HTML

理由とテーブルは memo/2026-06-18/plan-e-full-html-migration.md を参照。

ルールを覚えておけと自分に言い聞かせるよりも、ファイルに書いてClaude Codeに読み込ませる方が確実だった。次回コンテンツを追加するときに、Claude Code自身が「これは純粋テキストなのでHTMLで」と判断してくれる前提を作った。

Phase F-1は次セッションで中止判定が出た

午前のセッションを締める前に、次セッションへの引き継ぎプロンプトを書いた。「Plan E Phase F-1 を実行する。financial-statements の2件を外部HTML化してPRまで通す」。

午後のセッションでPhase F-1を始めたところ、ゲートチェックで思わぬ事実に当たった。対象の2件は表面的には解説テキストに見えたが、中を開くと useI18n + {{ t('...') }} mustache、slot scope <template #entry-image="{ entry }">、動的component が組み込まれていた。converterは変換前のVue構文検出でこれらを必ずrejectする。

F-2候補の4件も並列で読み直したら、全件 v-for + mustache を使っていて、3件は <Case100ChapterPager> という未登録カスタムコンポーネントまで持っていた。

Phase F-1 は中止、Plan E自体も一旦凍結した。コード変更はゼロ、handoff v13 と plan-e v2 を更新して締めた。

学びメモ

  • ルールがない状態は「中途半端」を生み続ける — 数字(Worker bundle margin)が安全圏に入っても、判断軸がなければ次の章で同じ問題が再発する。ルールを言語化してCLAUDE.mdに固定すると、判断のたびに人間が悩む必要がなくなった
  • 議論プロセスは別ドキュメントに残してCLAUDE.mdから参照する — ルール本文だけだと「なぜそう決めたか」が抜ける。memo配下に議論を残して参照を貼ると、後から読み返したときに前提が復元できる
  • 「純粋テキストに見える」と「実際に純粋テキスト」は別物 — ゲートチェックを通すまで判断を確定させないこと。i18nやslot scopeやmustacheが入っていれば、たとえ表示上は解説テキストでもVueに残すしかない
  • 計画と現実がズレたら、コードを動かす前に計画書を凍結する — Phase F-1 中止判定が出た瞬間、コードに手を入れず handoff と plan-e を更新するだけで終わらせた。判断結果を文書化するコストは、間違った実装を巻き戻すコストより小さい

明日以降にやること

  • Plan E の見直し: ゲートチェックで通る候補が現実にどれだけあるか、純粋テキスト化の方針を再評価する
  • 過去24件のHTML化を諦めるか、別のアプローチ(i18n含む章のHTML化基盤を作る)に切り替えるか判断する
  • CLAUDE.mdのルールが新規コンテンツ追加時に実際にワークするか、次の章追加で検証する