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 しません。履歴が分岐していたら停止するため、無人処理の入口に向きます。
用語表
| 用語 | この手順での意味 |
|---|---|
| clean | git status --porcelainに出力がない状態です |
| dirty | 追跡中の変更や未追跡ファイルがある状態です |
| fast-forward | 分岐を作らず、現在のbranchをリモートの先端まで進める更新です |
| draft | 生成済みですが、公開対象に含めない状態です |
| quality gate | commit前に必ず通す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.mdやCLAUDE.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.mjsがunknown 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 Tasks | fresh セッション、skill、workdir、cronの登録と実行 |
| Git公式ドキュメント: git-pull | pullの動作とfast-forward限定の更新 |
| Astro公式ドキュメント: Content Collections | collection schemaによるfrontmatter検証 |