Hermes Agentのcronで安全に書き込む|git pull・下書き・失敗停止を組み込む

朝 8 時の cron が記事を作り、検証して Git へ push します。ここまで自動化すると、便利さより先に「古い作業ツリーへ書かないか」が気になります。前日に別の人が更新した状態を読まずに始めれば、内容が正しくても運用は壊れます。

前回の「Hermes Agent を cron で自動運用する」では、スケジューラーの仕組みと登録経路を整理しました。今回は、Git 管理のリポジトリへ書き込むジョブを安全に閉じます。

前回から今回への差分

前回の完了時点では、cron を自律化の入口として捉え、CLI、チャット、TUI、Web ダッシュボードという 4 つの登録経路を把握しました。hermes cron createを実行するハンズオンや、Git への書き込み手順までは扱っていません。

今回の完了後は、Git 管理のブログへ下書きを書く場合に、次の順序を固定できます。

開始

  ├─ 作業ツリー確認 ─ dirty ─→ 停止

  └─ clean

       ├─ git pull --ff-only ─ 失敗 ─→ 停止

       └─ 同期成功

            ├─ 下書きを1件生成
            ├─ 検証
            ├─ draft確認
            └─ commit → push → 同期確認

なぜ生成前にGitを確認するのか

cron は、指定時刻に処理を始めるだけです。リポジトリが最新か、別の作業が途中かまでは保証しません。Hermes Agent の cron は毎回新しいセッションで動くため、現在の会話にある注意事項も自動では引き継ぎません。

書き込みジョブでは、生成品質より衝突回避を先に置きます。古い main から作った下書きは、同じ slug、変更済みのルール、更新された schema と衝突します。後から merge できても、二重生成や公開事故は元に戻せません。

git pull --ff-onlyは、fast-forward できない履歴を勝手に merge しません。履歴が分岐していたら停止するため、無人処理の入口に向きます。

用語表

用語この手順での意味
cleangit status --porcelainに出力がない状態です
dirty追跡中の変更や未追跡ファイルがある状態です
fast-forward分岐を作らず、現在のbranchをリモートの先端まで進める更新です
draft生成済みですが、公開対象に含めない状態です
quality gatecommit前に必ず通すschema検査、lint、型検査、buildです
fail closed条件を確認できないとき、処理を続けず停止する設計です

構成を3つの境界に分ける

書き込み cron は、準備、生成、確定の 3 段階に分けます。

Hermes cron

  ├─ 準備境界
  │    ├─ workdirを固定
  │    ├─ dirtyなら停止
  │    └─ pull --ff-only

  ├─ 生成境界
  │    ├─ 成果物は1件だけ
  │    ├─ draftを固定
  │    └─ 外部入力を確認

  └─ 確定境界
       ├─ 全ゲートを実行
       ├─ 差分を限定
       ├─ commitとpush
       └─ HEADとoriginを照合

すでに読み取り専用 cron を運用できている場合は、登録方法の説明を飛ばし、「書き込み前の停止条件」から始められます。

workdirを固定する

Hermes Agent の cron には、実行ディレクトリを指定できます。workdirを設定すると、その場所のAGENTS.mdCLAUDE.mdが読み込まれます。file や terminal などの操作先も同じディレクトリになります。

CLI で登録する例です。

hermes cron create "0 8 * * *" \
  "Git管理の下書きを1件作成し、検証後にpushする" \
  --workdir /home/me/projects/blog \
  --skill content-demand-draft \
  --name "morning-draft"

期待する確認結果は、一覧にジョブ名、時刻、workdir が表示されることです。

hermes cron list
morning-draft
Schedule: 0 8 * * *
Workdir: /home/me/projects/blog

表示形式は Hermes Agent の version で変わります。値が同じなら問題ありません。

書き込み前の停止条件を入れる

プロンプトの最初に、次の手順を置きます。

1. git status --porcelain を実行する
2. 出力が1行でもあれば、編集せず停止する
3. cleanなら git pull --ff-only を実行する
4. pullに失敗したら、編集、commit、pushを行わず停止する

手元で確認するときは、次の 2 コマンドを別々に見ます。

git status --porcelain
git pull --ff-only

clean なら 1 つ目は何も表示しません。同期済みなら 2 つ目は次のような結果になります。

Already up to date.

未追跡ファイルも dirty として扱います。「自分が作ったファイルではないから無視する」という判断を無人ジョブに任せません。

成果物を1件とdraftに固定する

「記事を作る」だけでは、本数と公開状態が曖昧です。プロンプトへ件数と禁止事項を入れます。

- 新規ファイルは1件だけ作る
- frontmatterは draft: true に固定する
- 既存記事の draft を変更しない
- 公開処理を実行しない
- 対象外のファイルを修正しない

Astro の Content Collections は、schema で frontmatter の形を検証できます。下書き側のキーを増やす前に、src/content.config.tsで許可されているか確認します。

生成後は、対象ファイルだけを検索します。

rg '^draft: true$' src/content/posts/new-post.md

期待出力です。

draft: true

0 件なら停止します。draftがない状態を公開扱いにする実装もあるため、未指定を許可しません。

hibanas-netのquality gateをcommitの前へ置く

push 後に問題が見つかるのは遅すぎます。次のコマンドは、この試作を置いている Astro 製ブログhibanas-netの検証例です。別のブログでは、そのプロジェクトの lint、型検査、build コマンドへ置き換えます。

node scripts/audit-frontmatter.mjs
node scripts/check-humanization.mjs
node_modules/.bin/textlint "src/content/**/*.{md,mdx}"
node_modules/.bin/astro check
node_modules/.bin/astro build

hibanas-net では、5 つすべてが exit code 0 になるまで commit しません。途中で失敗した場合は修正して最初から通します。原因を解消できなければ、成果物をリモートへ送りません。

検証後は差分の範囲も確認します。

git status --short
git diff -- src/content/posts/new-post.md

期待する差分は、新しい下書きと、その下書き専用の画像などに限ります。設定ファイルや既存記事が混じったら停止します。

push後も完了判定をする

git pushが exit code 0 でも、最後の確認を残します。

git push origin main
git status --short --branch
test "$(git rev-parse HEAD)" = "$(git rev-parse origin/main)"

期待する状態です。

## main...origin/main

差分表示がなく、HEAD とorigin/mainが同じなら、下書きの受け渡しまで完了です。公開は別の承認工程で行います。

プロンプトを自己完結させる

cron の実行セッションには、現在のチャット履歴を前提にできません。最低でも次をプロンプトか skill へ残します。

  • 絶対パスの workdir
  • 生成件数
  • draft: trueの固定
  • 対象と対象外
  • 実行する quality gate
  • 失敗時に commit しない条件
  • push 後の確認方法
  • 報告するファイルパス

人が読めば分かる「いつもの手順」は、無人ジョブには存在しません。条件を文章ではなく、コマンドと判定結果まで書くと再現しやすくなります。

よくあるエラー

症状原因対応
git pull --ff-onlyが失敗するlocalとremoteの履歴が分岐しています自動mergeせず停止し、人が履歴を確認します
実行のたびに別の場所へ書くworkdirが未指定です絶対パスでworkdirを設定します
node scripts/audit-frontmatter.mjsunknown keyで失敗するfrontmatterに監査スクリプトで許可されていないキーがあります許可済みのキーへ戻し、schemaを勝手に広げません
下書きが公開一覧へ出るdraftがないか、filterが未実装ですschemaと一覧取得処理の両方を確認します
2件以上生成される本数が「複数」など曖昧です「新規1件だけ」とファイル数の確認を入れます
push後もbranchがbehindになる作業中にremoteが更新されました再pushせず停止し、次の同期方法を人が決めます
quality gateが一部だけ通る途中の失敗を無視しています&&または個別exit codeで全成功を条件にします

今回の到達点

時刻どおりに動く cron と、安全に書き込める cron は別物です。後者には、開始前の clean 判定、fast-forward 限定の同期、成果物 1 件、draft 固定、commit 前の全検証、push 後の照合が必要です。

この形なら、失敗した朝は何も公開せず止まります。成功した朝だけ、レビュー可能な下書きが 1 件リモートへ届きます。自動化では、毎回成功することより、失敗時に余計な変更を残さないことを先に設計します。

出典

一次情報確認した内容
Hermes Agent公式ドキュメント: Scheduled Tasksfresh セッション、skill、workdir、cronの登録と実行
Git公式ドキュメント: git-pullpullの動作とfast-forward限定の更新
Astro公式ドキュメント: Content Collectionscollection schemaによるfrontmatter検証
この記事をシェア