この記事で解決できること
- Qiita CI が frontmatter 不備で止まり続ける問題の根本解決策
- LLM 生成コンテンツを push する前に決定論的に検証するゲートパターン
- validate-frontmatter.sh の実装方針と、どの自動化フローに組み込むかの判断基準
環境
- 自動化ルーティン: Claude (claude-sonnet-4-6) + GitHub Actions
- Qiita CLI: qiita-cli v0.5.0+
- Shell: bash
問題: 10日・14回連続 CI 失敗
2026-05-07 から 2026-05-17 の10日間、Qiita の publish CI が連続失敗しました。
直接原因は2つ:
-
updated_at/idフィールドの欠落 — Qiita CLI はupdated_at: 'ISO8601'(シングルクォート必須)とid: ''を必須とするが、LLM が生成した frontmatter でこれらが欠落 -
published_atフィールドの残存 — qiita-cli v0.5.0+ で非認識フィールド。LLM が古いフォーマットを生成し続けた
--all モードのため、3記事の欠落が全件停止を引き起こしました。
疑った原因と外れた仮説
最初は「プロンプトのフォーマット指定が足りない」と思いました。3回プロンプトを書き直しましたが、失敗は続きました。
外れた仮説: 「より詳しいプロンプトで解決できる」
実際の原因: LLM はテキスト指示から完全に外れることを確率的に必ずやる。自動化ルーティンが繰り返されるほど、低確率の逸脱が現実の障害になる。
解決策: push 前の決定論的ゲート
実装した validate-frontmatter.sh の主要チェック項目:
# updated_at: シングルクォート必須
if ! echo "$head" | grep -qE "^updated_at: '"; then
echo "::error file=$f::updated_at が欠落 or single quote 未付与"
fail=1
fi
# id: 必須(新規は id: '')
if ! echo "$head" | grep -qE "^id:"; then
echo "::error file=$f::id が欠落"
fail=1
fi
# published_at: 禁止フィールド
if echo "$head" | grep -qE '^published_at:'; then
echo "::error file=$f::published_at は非認識フィールド、削除してください"
fail=1
fi
content-executor の Qiita push 前にこれを実行し、NG なら push をスキップ:
bash scripts/validate-frontmatter.sh public/${slug}.md || {
echo "frontmatter NG: push をスキップします"
exit 0
}
まとめ: ゲートを置く判断基準
自動化フローのどこに検証ゲートを入れるか、以下の3点で判断します:
- 下流が構造依存かどうか: CLI・API・パーサーが特定のフォーマットを前提とするなら、生成→消費の境界にゲートが必要
-
失敗のコストが広がるかどうか: 1件の不備が全件停止を引き起こす設計(
--allモードなど)は優先度を上げる - 修正ループに人が入れるかどうか: 人がいない時間に動くルーティンは、人が介入する前に止まるゲートが必要
LLM の生成品質を「より良いプロンプト」で担保しようとするのは、長期的には機能しません。生成直後・push 直前に決定論的な検証を噛ませ、NG なら処理を止める設計が安定稼働の鍵です。
AI ツールを業務に組み込む設計の実験ログを書いています。 https://masatoman.net