life-plan を Markdown 正本の個人計画アプリとして拡張した実装
life-plan は、個人の目標や計画を Markdown で管理しながら、Web UI でも見返せるようにするための個人用リポジトリである。今週はこのリポジトリに対して、Markdown を唯一の編集元に保ったまま、短期計画だけでなく長期目標、週間・月間・習慣ビュー、さらに「今は優先しないが残しておきたい構想」を扱う画面まで広げた。
この変更のポイントは、画面を増やしたこと自体ではない。Markdown 正本、生成済み TypeScript、Next.js 16 の表示層、Cloudflare Workers 向けのビルド運用を分離し、計画内容の更新とアプリ実装の変更を切り分けられる構成へ寄せたことにある。この記事では、life-plan を「Markdown を読むだけの試作」から「継続運用しやすい個人計画アプリ」へ拡張した過程を整理する。
この記事で解決すること
- Markdown で計画を管理しつつ、Web UI でも見返せる構成にしたい。
- 短期、長期、週間、月間、習慣、将来構想を同じアプリ内で扱いたい。
- 文章を正本にしたまま、テストとビルドで更新漏れを検出できる形にしたい。
実装の要点
今週の変更は大きく 3 つある。1 つ目は、plans/2026-07-27_to_2026-10-31.md と plans/long-term.md をもとに、短期計画ページと長期目標ページを持つ web/ アプリを追加したことだ。Markdown から生成済み TypeScript へ同期する仕組みを作り、Next.js 16 ベースの UI から読み込めるようにした。
2 つ目は、同じ同期パターンを週次・月次・習慣ビューへ広げたことだ。plans/weekly.md、plans/2026-08.md、docs/habits.md を入力にし、共通の CadencePage で /weekly、/monthly、/habits を表示できるようにした。新規ルート追加時に 404 が出る問題もテストで検知し、ルーティングと描画の両方を揃えた。
3 つ目は、plans/someday.md を追加し、「やりたいこと」ページ /someday を同じ導線へ組み込んだことだ。あわせて docs/deep-dive-backlog.md を整備し、README から深掘り資料へ辿れるようにした。最終的に lint、テスト、ビルド、GitHub 反映、配布用アーカイブ生成まで進めている。
得られた知見
最も大きい成果は、life-plan が「Markdown を置いておく場所」ではなく、「Markdown を正本にしたまま複数の計画ビューを安定表示できるアプリ」になったことだ。短期と長期だけでなく、週間、月間、習慣、やりたいことまで、同じ同期ルールとナビゲーションで扱えるようになった。
検証面でも前進がある。ワークログからは、Markdown パーサーの単体テスト、HTML 描画テスト、npm --prefix web run lint、npm --prefix web test、npm test、vinext build の成功が確認できる。つまり、コンテンツ変換、画面描画、ビルドの各段階を別々に壊れ方を追える構成まで整った。
運用面では、Add someday goals for entrepreneurship というコミットで GitHub の main へ反映し、さらに配布用アーカイブ生成まで完了している。一方で、プライベートホスティングへの最終公開完了までは、今週の一次証跡だけでは断定していない。ここで言える成果は、リリース可能な形へ寄せたことまでである。
実装プロセス
最初に行ったのは、Markdown を直接 UI が読む構成を避けることだった。短期計画と長期目標は更新頻度も構造も異なるため、ランタイムで Markdown を解釈させると表示ロジックと入力仕様が密結合になる。そこで、パーサーと同期スクリプトを用意し、ビルド前に generated ファイルへ変換する流れを先に固めた。
その後、同じ流れを cadence 系のページへ展開した。週間、月間、習慣の各ページは情報の粒度が違うが、見出し、説明、チェック項目という共通構造で扱える。共通パーサーと共通 UI を置くことで、新規ルート追加時に必要な作業を「Markdown 追加」「同期対象追加」「route 追加」「テスト追加」に限定した。
最後に、「やりたいこと」ページを追加した。これは短期目標へ混ぜると現在の優先順位を汚しやすいため、将来構想だけを別ページに逃がす判断だった。同時に深掘りバックログも docs へ分離し、日々見る計画と、時間をかけて考える材料を別管理にした。ここまで揃えてから総合テストを回し、GitHub 反映と配布準備へ進んでいる。
利用した技術
- 対象アプリは個人計画アプリ
life-plan、対象リポジトリも同名のlife-planである。web/の表示基盤にはNext.js 16、React 19、TypeScript、vinextを使い、Cloudflare Workers / Sites 向けのビルド前提を維持した。 - アプリ変更では
app/page.tsx、app/long-term/page.tsx、app/someday/page.tsx、app/components/CadencePage.tsx、app/components/CadenceChecklist.tsx、app/components/PlanNav.tsx、app/globals.cssを更新し、短期・長期・週間・月間・習慣・やりたいことの各ビューを実装した。 - 実装では
scripts/lib/plan-parser.mjs、scripts/lib/long-term-parser.mjs、scripts/lib/cadence-parser.mjs、scripts/sync-plan-content.mjs、scripts/sync-long-term-content.mjs、scripts/sync-cadence-content.mjsを追加・更新し、Markdown を generated TypeScript へ同期する契約層と I/O 層を分けた。 - コンテンツ入力として
plans/2026-07-27_to_2026-10-31.md、plans/long-term.md、plans/weekly.md、plans/2026-08.md、plans/someday.md、docs/habits.md、docs/deep-dive-backlog.mdを使い、README から深掘り資料へ入れる導線も整えた。 - テストでは
tests/plan-parser.test.mjs、tests/long-term-parser.test.mjs、tests/cadence-parser.test.mjs、tests/rendered-html.test.mjsを使い、パース仕様とビルド後 HTML の両方を確認した。実行結果としてnpm run lint、npm --prefix web run lint、npm --prefix web test、npm test、vinext buildの成功がワークログに残っている。 - リリース運用では GitHub の
mainへの push、配布用アーカイブ生成、.openai/hosting.jsonを含む Cloudflare 向け成果物構成の維持が確認できる。ただし、プライベートホスティングへの最終 deploy 完了は今週の確認範囲では未確定である。
アーキテクチャ
今回の構成は、入力層、変換層、表示層、配備層の 4 段に分けると理解しやすい。入力層は plans/ と docs/ の Markdown 群で、人間が直接編集する正本である。変換層は各 parser と sync スクリプトで、Markdown を UI が読みやすい generated TypeScript へ変換する。表示層は Next.js 16 の route と共通コンポーネントで、generated データを描画する。配備層は vinext と Cloudflare 向け成果物生成で、ローカルビルドから公開準備までを支える。
flowchart LR
A["Markdown 正本<br/>plans / docs"] --> B["parser 群<br/>plan / long-term / cadence"]
B --> C["sync スクリプト<br/>generated TypeScript"]
C --> D["Next.js 16 画面<br/>short-term / long-term / weekly / monthly / habits / someday"]
D --> E["vinext build"]
E --> F["Cloudflare 向け成果物<br/>配布・公開準備"]重要なのは、UI が Markdown の生テキストや見出し規則に直接依存していないことだ。Markdown 仕様の変更はまず parser とテストで受け止め、画面側は generated データの契約だけを読む。この分離によって、入力フォーマットの変更、ページ追加、ホスティング設定変更の影響範囲を切り分けやすくしている。
選定理由
Markdown を正本にしたのは、個人計画の更新頻度が高く、フォームや DB を先に作るよりもテキスト編集と Git 差分の方が運用しやすいからである。特に短期計画、長期目標、週間レビューのように更新サイクルが違う情報は、文書として持つ方が変更理由を追いやすい。
その一方で、ランタイムで Markdown を直接読む構成は採らなかった。Cloudflare Workers 向けに配るアプリでは、依存と処理を軽く保ちたい。そこでビルド前に generated TypeScript へ変換し、表示層を静的データ参照だけに寄せた。この判断によって、アプリ実装、テスト、ビルド、配布準備の責務を分けやすくなった。
週間・月間・習慣・やりたいことを個別 UI にしすぎず、CadencePage とナビゲーションを共通化したのは、ページ追加ごとに実装とテストが散らばるのを避けるためである。新しいカテゴリを足すたびに route だけ増やし、構造は共通化する方が保守しやすい。
GitHub 反映と配布用アーカイブ生成まで進めたのは、このアプリを継続運用する正本として扱っているためである。ただし、公開確認が未取得の段階で「deploy 完了」とは書かず、実装済み、テスト済み、ビルド済み、release 準備済みを分けて記述している。
導入手順
- まず、表示したい計画を
plans/またはdocs/の Markdown として定義し、見出しやチェックリストの構造を揃える。短期計画、長期目標、週間・月間・習慣、将来構想を別ファイルに分ける。 - 各入力形式に対応する parser を実装し、Markdown から期間、カテゴリ、説明文、チェック項目を抽出できるようにする。今回の構成では
plan-parser.mjs、long-term-parser.mjs、cadence-parser.mjsがこの役割を担った。 - parser の出力を generated TypeScript に書き出す sync スクリプトを用意し、
predevとprebuildから必ず呼ばれるようにする。これで開発時と本番ビルド時の同期漏れを防げる。 Next.js 16側では generated ファイルを読む page と共通コンポーネントを実装する。短期・長期の専用ページと、週次・月次・習慣・やりたいことを扱う共通CadencePageを分けると整理しやすい。- parser の単体テストを追加し、Markdown 構造が崩れたときに早く失敗するようにする。さらに rendered HTML テストを入れて、ルート追加漏れや 404 を検知できるようにする。
- 検証では
npm --prefix web run lint、npm --prefix web test、npm test、vinext buildを順に通し、同期、描画、ビルドの全体を確認する。ページ追加時は既存ルートの回帰も見る。 - リポジトリ反映が必要なら、変更を GitHub の
mainへ push し、必要に応じて配布用アーカイブを生成する。Cloudflare 向けの公開作業は、その後に別工程として完了確認まで取る。
導入時の課題
最初の課題は、Markdown の自由度と UI が必要とする構造の間にズレがあることだった。見出しやチェックリストの書き方が揺れると、パーサーが安定して情報を取り出せない。Markdown を正本にする以上、構文ルールの明文化とテストによる固定が必要だった。
次の課題は、新しい route を増やすだけでは画面が成立しないことだった。週間・月間・習慣ページ追加時には、初期状態で 404 を返す問題が出ており、同期、route、ナビゲーション、描画テストを同時に揃える必要があった。ページ追加を軽い作業と見なせない構造であることが分かった。
また、generated ファイルが増えるほど同期漏れのリスクも上がる。短期、長期、cadence 系の同期スクリプトが別々に存在するため、predev と prebuild に組み込んでおかないと、ローカルでは見えてもビルド成果物が古いまま残る可能性がある。
最後に、配布準備までは進めても、最終 deploy 完了の証跡は別に必要だった。GitHub 反映やアーカイブ生成が済んでいても、公開確認を取らない限り「運用完了」とは言えない。この線引きをワークログと記事本文の両方で維持する必要があった。
次に検討した方が良いこと
- Markdown の構文ルールを README や専用ドキュメントに明文化し、編集時に崩しやすい見出しパターンを先に制約する。
content:syncの対象一覧をより宣言的に管理し、新しい計画カテゴリ追加時の実装箇所を減らす。- generated ファイルの更新漏れを CI で検知し、ローカルで同期したつもりの差分が push 後にずれないようにする。
- Cloudflare 向け公開作業では、build 成功だけでなく配備後の疎通確認と画面確認を自動化し、release 完了の証跡を残しやすくする。
/somedayや deep-dive backlog のような将来テーマに更新日やレビュー周期を持たせ、情報が古いまま残るのを防ぐ。