手書き HTML ブログの index.html 競合から逃げる — 記事メタを YAML に分離して一覧を自動生成するかの判断

AI 主体で執筆

困る場所は 2 つある:コンフリクトと追加忘れ

このブログは手書きの静的 HTML で運用していて、記事を 1 本追加する手順は単純です。

  1. blog/<slug>/index.html を新規作成
  2. blog/index.html(一覧ページ)に <article class="post"> を 1 行追加
  3. 必要なら backlog/ideas.md の該当エントリを消す

問題は 2 番目で、記事を並行で書いていると毎回ここで事故が起きます。

事故 1:コンフリクト

複数の feature ブランチが同じ blog/index.html の同じ場所(日付降順なので先頭近く)に記事行を追加します。main へ順次マージしていくと、2 本目以降は必ずコンフリクトします。本ブログでも、feat/24-blog-xcode-xcstrings-silent-drift を rebase したときにまさにこれが起きて、手で記事行を挿入し直しました。

事故 2:追加忘れ

同じ feat/24 では、記事 HTML は足したのに一覧への追加を忘れていました。記事ページ単体は公開されているのに一覧から辿れない、という状態で一度 push されていた。リバースで気付けてよかったですが、検索からしか来ない記事になりかけました。

これはコンフリクトとは別種の困りごとで、手で 2 箇所を編集する運用は「片方を忘れる」リスクを常に抱えます。

現時点の対処(ルール化)

応急処置として、本ブログの .cursor/rules/blog-ai-disclosure.mdc に新規記事公開時のチェックリストを追加しました。

## 新規記事を公開するときのチェックリスト

新規記事 `blog//index.html` を作るときは、同一 PR で以下を必ず満たす。

- [ ] 記事本体 HTML
- [ ] `blog/index.html` の記事一覧に行を追加(日付降順で挿入)
- [ ] `
` に `

これで「追加忘れ」は相当防げますが、コンフリクトはルールでは消せません。ルールで守れない構造的な問題が残っている、という自覚は持っておきたいところです。

本質:一覧情報が記事ファイルの外に存在している

なぜ毎回同じ事故が出るのかを一段抽象化して見ると、記事のメタ情報(タイトル・日付・説明)が記事ファイル自体と一覧ファイルの 2 箇所に存在していることが根本です。両方に書く手作業があるから、追加漏れとコンフリクトが起きます。

理想は、メタ情報を記事ごとに 1 箇所に寄せて、一覧ページはそれを読んで組み立てる仕組みにすることです。記事 HTML の <meta><title> をパースして一覧を生成する案と、記事ごとに post.yml を置いてそれを source of truth にする案が考えられます。

案 A:記事 HTML をパースして一覧を生成

記事ファイルの <title><meta name="description"><time datetime> を読み取って一覧ページを組み立てます。

案 B:記事ごとに post.yml を置く

blog/<slug>/post.yml にメタ情報を書き、記事 HTML と一覧はそれを別々に参照します。

# blog/static-blog-index-autogen-from-yaml/post.yml
title: 手書き HTML ブログの index.html 競合から逃げる
date: 2026-04-19
slug: static-blog-index-autogen-from-yaml
description: 記事メタを YAML に分離して一覧を自動生成するかの判断
tags: [60d.dev, meta, static-site]

どちらも一覧生成は Netlify の build コマンドで Node スクリプト(あるいは Python)を回す形になります。今の本ブログは HTML を push しただけでデプロイされるビルドレス運用なので、どちらの案でも build ステップの導入は必須です。

副次効果:AI バッジのような横断埋め込みを自動化できる

もう 1 つ、ビルド導入で解ける問題があります。AI 主体で書くブログの開示は免責ではなく UX 設計だった で書いたように、本ブログでは全記事のヘッダーに AI 主体で執筆 バッジを入れています。

今は、新規記事を書くたびにバッジ HTML と CSS を手(あるいは Cursor Rules 経由)で毎回挿入しています。過去 17 記事に対して一斉に追加したときも、1 記事ずつ手を入れました。もしメタ情報とテンプレを分けていれば、この種の横断変更は テンプレ 1 箇所の変更 で全記事に適用できます。

つまり、ビルド化の利点は「一覧生成」だけではなく、将来の横断変更に対する耐性にもあります。5 年運用して 50 記事を超えた頃に「footer にニュースレター登録を入れる」「記事末尾に類似記事を自動表示する」といった変更が来たとき、50 記事を全部手で触るコストが跳ね上がります。

今やるべきか:判断軸

ここからが本題です。ビルド化は正解側の方向ですが、今やるかは別の判断で、記事 18 本の時点で踏み切るべきか、もう少し今の運用のまま先送りすべきか、が問われます。

現状値 踏み切る目安
記事数 18 本 30 本前後(横断変更が重くなってくる規模)
並行執筆頻度 1 回/月 程度でコンフリクト発生 1 回/週 になったら黄信号
追加忘れ回数 これまでに 1 回 2 回目が出たらルールでは防げてないと判断
横断変更コスト 17 記事一斉変更を 1 日で完了 1 日で終わらなくなったら危険水域
ビルド導入の心理的ハードル 高(今はビルドレス) —(コスト side ではなく constraint side)

今回の判断:延期する

4 つのうち 3 つがまだ目安に届いていません。記事数 18 本、並行執筆は月 1 回程度、AI バッジ一斉追加は 1 日で片付いた規模感で、踏み切るには不便さが軽く、ルール化(新記事チェックリスト)で十分しのげる段階だと判断しました。

踏み切るタイミングは、これまで記事を書いてきた感覚では、次のどれかがトリガーになると見ています。

どれか 1 つで踏み切る、2 つ重なったら即実施。そう決めておけば、あとは普通に記事を書き続けるだけで、必要なタイミングに自動的に意思決定が走ります。

原則:構造的な問題で重視するのは「今の不便さ × 回避コスト」より「将来の不便さ × 踏み切るコスト」のほうです。不便さが小さいうちはルールでしのぎ、目安ラインを超えたら一気に仕組みごと作り直す。

先送り中の守り:構造を壊れにくくする diff-friendly な書き方

延期する以上、延期期間中にさらなる事故を呼ばない守りを入れておきたいところです。ビルド化せずにコンフリクトを減らす方法がいくつかあります。

記事行を常に最上行に追加する

現状、日付降順で並んでいるため、新しい記事は必ず一覧の先頭に挿入されます。複数のブランチが 同じ「先頭」 に挿入しようとするから、行単位で見るとコンフリクトになります。

対策として、記事一覧の冒頭にマーカーコメントを置いておきます。

<!-- new-articles:above -->
<article class="post">...</article>

こうするとマージツールはマーカーを anchor として認識しやすくなり、3-way merge の解消がだいぶ機械的に進められます。ただし常に必ず解消されるわけではありません。

rerere で解消パターンを Git に覚えさせる

関連記事(Cursor を複数走らせると git stash が勝手に増え続ける)で書いた git config rerere.enabled true は、ここでも効きます。一度解消すれば、同種のコンフリクトが次回以降自動解消されます。

1 PR には 1 記事、できれば 2 記事まで

今回の 3 記事バッチ PR のように複数記事をまとめる運用は、自分のブランチ内では矛盾が出ないので問題ありません。ただし並行で他の記事 PR が走ると、バッチ PR 側がマージされた瞬間に他の PR が必ず rebase 必須になります。衝突が一気に来るので、並行執筆を前提にする期間はバッチ化も控えめにしておくのが安全です。

まとめ

「今の不便さ × 回避コスト」だけで考えるとルール追加でしのぎがち、「将来の不便さ × 踏み切るコスト」を合わせて見ると設計変更に踏み切りやすくなります。基準を数値で決めておけば、どちらに倒すかの毎回の判断を省けます。

次に本ブログでビルド化する日は、この記事に追記する形で記録する予定です。

関連記事