前回の記事で、このブログをWordPressからAstroに移行した話を書きました。ブログとしてはひとまず動いていますが、Astroをほぼ素の状態から組んだので、まだ改善できることが残っているはずです。この記事は、移行直後のブログをAIエージェント(Claude Code)と一緒に0ベースで総点検し、見つかった問題を43本のPRで直していった作業の記録です。ここで言う「0ベース」は、「WordPress時代はこうだった」という前提を一度外して、入れるべきもの・直すべきものをゼロから網羅的に洗い出す、という意味で使っています。
きっかけ
移行は一段落しましたが、WordPressのプラグインでやっていた機能は移行時にすべて消えています。Astroのデフォルトでカバーされているものもあるはずですが、何が消えて何が残っているかを手動で全部洗い出すのは非現実的です。かといって、せっかくAstroに移行したのに旧環境を全部ベタ移植すると、またメンテナンスできない状態に逆戻りしそうです。
そこで、アイデア出しはClaude Codeに任せて、何をやるかは自分で決めることにし、こう依頼しました。
このブログに過去に導入していたWP拡張等も含めたうえで、入れたほうがいい・検討すべきプラグイン or 改修がないか網羅的に調べてください
スタートの依頼はこの1行だけです。ここから3日間の総点検が始まりました。使用モデルは主にClaude Fable 5で、定額プランの利用上限に達してからはOpus 4.8に切り替えています。3日間といっても張り付きで作業していたわけではなく、依頼を投げて放置し、合間に上がってきたものをレビューする、というスタイルです。
監査の進め方
エージェントはまず、調査を4つの観点に分けて並列で進めました。
- フロントエンド / SEO
- インフラ / セキュリティ
- CI/CD / 自動化
- コンテンツ整備 / メディア処理
それぞれの観点をサブエージェント(メインの会話とは別に動く調査担当)に割り当てて同時に調べさせ、結果を1つの改善リストに統合する、という流れです。人間側(私)の仕事は、上がってきたリストに優先度をつけて「すぐ直す」「Issueにして後回し」「やらない」を判断することでした。
このとき決めた運用ルールが、後から効いてきます。
- すぐ直せるものは小さいPRに分割し、1PRずつレビューしてマージする
- 実装が終わったPRには、マージ前に「PR #XX を敵対的検証して」とエージェント自身にレビューさせる(効果は後述のtextlint導入の項で触れる)
- すぐやらないものはGitHub Issueにする。ただしIssueには調査済みの実行計画・判断ポイント・完了条件まで書き切る(後で別セッションのAIが読んでも作業を再開できる状態にする)
「やらない」は結局使わなかった
ここは私の個人的な性分なのですが、「今はやらないにしても後でやるかも…」と思ってしまってcloseの判断がしづらいので、よほど的外れな提案でなければすべてIssueにするつもりで進めました。結果として、明らかに的外れな提案はなかったので、すべてIssue化しています。
(業務でこれをやるとIssueが際限なく増えて管理できなくなるので自分でもやりたくないのですが、個人ブログのプロジェクトなので性分ベースで進めています)
見つかった問題たち
プラグイン由来の機能がいくつか失われることは移行前から予期していました。ただ、記事そのものはAstroでゼロから組み直したので、「表示が壊れている」と呼べるものまでは残っていないはずだと思っていました。ところが実際には次から次へと問題が見つかり、純粋な改善(テスト追加や日本語校正のlintなど)も含め、最終的に43本のPRになりました。代表的なものを観点別に挙げます。
SEO・フロントエンド
以下はいずれも、個別のPRで実装・修正済みです。
- 構造化データ(JSON-LD)が未実装
- sitemapに更新日時(lastmod)が出力されていない
- OGPメタタグの不足と、og:typeの二重出力
- 関連記事機能がない(WordPress時代はプラグインで表示していた)
- theme-colorが動的にしか設定されず、ページ読み込み時にアドレスバーの色がちらつく
セキュリティ・インフラ
- セキュリティヘッダ(X-Content-Type-Options等)が一切ない(一式追加して解消)
- Content-Security-Policyの未導入(これは影響が大きいのでIssue化し、Report-Onlyから段階導入する計画に)
CI/CD・自動化
- ユニットテストが存在しない(Vitestを導入)
- 予約投稿ができない(静的サイトの宿命。GitHub Actionsで「公開されるべき記事が本番サイトのsitemapにないときだけ再ビルドを起動する」workflowを自作)
- 日本語記事の校正チェックがない(後述のtextlint導入へ)
コンテンツの破損・劣化
移行記事で「差分チェックで壊れた箇所を見つけた」と書きましたが、監査ではさらに見つかりました(いずれも修正済みです)。
- Gist埋め込みの破損URLと、埋め込みスクリプトの残骸
- WordPressプラグインのショートコード残骸(コードタグ、Amazonアフィリエイトのrcm等)
- サービス終了したGoogle Charts APIの画像(描画パラメータから画像を作り直して救済)
- 画像にlazy loading属性と寸法指定がなく、レイアウトシフトの原因に
- そして最大の負債が、次に書くdescriptionでした
96記事のdescriptionを書き直す
このブログの記事には、検索結果やOGP・RSSに使われる要約文(description)が設定されています。ところが移行時の自動生成では、本文の冒頭を約101文字で機械的に切って「…」を付けただけのものが96記事に入っていました。多くが文や単語の途中でぶつ切りです。
これを「この記事で何が分かるか」の完結した要約に書き直す作業を、AIエージェントとこう進めました。
- 複数のサブエージェントに記事本文を分担して読ませ、要約案を生成する
- メインのエージェントが文体と事実関係を整え、frontmatterのdescriptionだけを書き換える(本文には一切手を入れない)
- 10〜20記事ずつのPRに分割し、PR本文に「旧→新」の対比表を載せる
- 人間は対比表を眺めてレビューし、マージする
96記事を一括で出されたらレビューは破綻していたと思いますが、対比表つきの小さいPRが5本に分かれていたことで、1本あたり数分で確認できました。AIに大量作業を任せるときは、作業そのものより「人間がレビューできる形に区切る」設計の方が大事というのが実感です。
textlintで「新しい記事だけ」校正する
日本語の校正チェック(textlint)も、CIに入れました。ここで問題になるのが、2012年から書き溜めた過去記事の存在です。旧文体の記事を全部チェックすると千件以上の指摘が出て、CIが赤いままになります。
そこで次の設計にしました。
- 過去記事(アーカイブ)はチェック対象から除外する
- CIではPRで変更された記事だけにtextlintをかける
- 表記ゆれ辞書は自前で全部管理せず、公開辞書(
WEB+DB_PRESS.yml)をベースに、ブログ固有の語彙だけ自前ルールで足す
「変更された記事だけ」のCIステップは、実質これだけです。
- name: "📝 textlint (changed posts only)"
if: github.event_name == 'pull_request'
run: |
set -o pipefail
# fail-open防止: HEAD^1が取れない(fetch-depth不足等)ときはスキップせず失敗させる
git rev-parse -q --verify 'HEAD^1' > /dev/null
files=$(git -c core.quotepath=false diff --name-only --diff-filter=d HEAD^1 HEAD \
-- 'src/content/posts' ':!src/content/posts/_archive' | { grep '\.md$' || test $? -eq 1; })
if [ -z "$files" ]; then
echo "記事の変更なし: textlintをスキップ"
else
echo "$files" | xargs -d '\n' npx textlint
fi
導入時に興味深かったのは、運用ルールに書いた敵対的検証レビューです。この運用は、ちょうどXで以下の記事を見かけたのがきっかけでした。
このPRでも実装後に「敵対的検証して」と投げたところ、「アーカイブ記事のfrontmatterだけ直すPRでもCIが落ちる」「差分取得に失敗するとチェックが静かに無効化される(fail-open)」という、放置すれば将来確実に踏んでいた問題を2件掘り当てて、自分で直しました。上の抜粋にある git rev-parse の行が、まさにそのfail-open対策です。
公開辞書には罠もありました。辞書の「Markdown」ルールがハイフンつきのツール名(wordpress-export-to-markdown)の一部まで「修正」してしまったのです。公開辞書のルールをそのまま使うだけではなく、自分の環境で使って問題ないかをルール単位で確認する、という教訓になりました。
ドキュメントの嘘を直す
監査の副産物として、「ドキュメントと実態の矛盾」も見つかりました。このブログのリダイレクト設定ファイルには「手編集禁止。変更時は移行スクリプトで再生成すること」と書いてありましたが、よく調べるとその再生成は移行元データが手元にないため実行不可能で、仮に実行できても移行後に直した記事を全部上書きしてしまう危険なものでした。
「ドキュメントに従うと事故る」状態だったので、実態に合わせてルールを書き直しました。生成物だからと安心せず、その生成手順が今も再実行可能かを疑うのは、移行プロジェクト全般で使える点検項目だと思います。
余談: 長すぎたセッションが暴走した
最終的にこのブログ記事の材料にしたかったこともあり、監査作業は当初、1つのセッションで通して進めていました。ところが、Opus 4.8に切り替えた後、大きなセッションとして続けていたせいか、以下の記事で紹介されているような、同じ自己弁明や出力を延々と繰り返して先へ進めなくなる状態に陥ってしまいました。
この状態になると、どう指示しても余計な出力を消せなかったため、諦めてセッションを終了し、新規セッションで続きを進めました。前述のとおり残タスクは「別セッションのAIが読んでも再開できる粒度」でIssue化してあったので、切り替えによる損失はほとんどありませんでした。
この記事を書くときも、古いセッションと新しいセッションの両方のセッションIDを渡して「そこでやったことを記事用にまとめて」と指示するだけで、問題なく作業内容を認識してくれました。
結果と残り
3日間の総点検の結果です。
- 最終的にマージしたPR: 43本(SEO・セキュリティヘッダ・テスト・予約投稿・コンテンツ修復・画像圧縮・description書き直し・textlint CI等)
- Issue化して計画的に残したもの: CSPの段階導入、コメント機能(giscus)、リンク切れチェックCI、画像のastro:assets移行、RSS全文配信など
Astroに移植したら終わりだと思っていましたが、WordPressのプラグインが暗黙にやってくれていたことは想像より多く、GitHubでCIを回せる環境になったからこそやりたいことも増え、欲しいものはどんどん積み上がっていきました。ただ、この手の置き換え・追加作業はAIエージェントとの相性が非常に良く、「網羅的に調べて」の1行から始まった監査は、人間1人では確実にやり切れなかった量の改善を3日で消化してくれました。