0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Rulerで32エージェントにルールを一括配布したら、revertで手書きAGENTS.mdが消えた

0
Posted at

はじめに

対象読者

CLAUDE.mdAGENTS.md.clinerules.github/copilot-instructions.md に、ほぼ同じルールを別々のフォーマットで書き写している方に向けた記事です。

Ruler(MIT ライセンス・stars 2,824)は、この重複を「.ruler/ に 1 回書けば全エージェントへ配布する」形で解消する CLI です。実際に Linux 環境へ導入して 32 エージェント分の設定を生成し、後片付けの ruler revert まで通したところ、導入前から存在した手書きの AGENTS.md が復元されずに削除される 挙動に行き当たりました。

この記事で分かること

  • .ruler/ の 1 ソースが、どのエージェントのどのファイルへ展開されるか(実測 19 出力先)
  • ruler apply.gitignore に何を書き込むか(/CLAUDE.md を含む 48 パス)
  • ruler revert で手書きファイルを失う条件と、--keep-backups による回避

前提環境

  • OS: Linux 6.18.5(x86_64)
  • Node.js: v22.22.2
  • @intellectronica/ruler: 0.3.44(npm view 時点の latest)
  • 検証日: 2026-07-27(JST)

TL;DR

  • ruler apply は 1 ソースを 32 エージェント・19 出力先へ展開し、.gitignore の Ruler ブロックに 48 パスを追加します。/CLAUDE.md/AGENTS.md もそのブロックに入ります。
  • ruler revert(既定)は CLAUDE.md.bak から復元する一方、リポジトリ直下の AGENTS.md は「生成物」として削除 しました。2 リポジトリで再現しています。
  • 回避策は ruler revert --keep-backups です。復元件数が 2 件から 18 件に変わり、AGENTS.md も原本のまま残りました。
  • ruler --version は ruler 自身ではなく、実行元プロジェクトの package.json の version を表示します。バージョン確認は node -p で package.json を読む方が確実です。

Ruler がやっていること

Ruler の中心は「1 ソース多出力」です。.ruler/AGENTS.md にルールを書き、ruler apply を実行すると、各エージェントが読む場所へ同じ内容が転記されます。

図の左上に AGENTS.md が 2 つ出てくる点が、後半のハマりポイントに直結します。リポジトリ直下の AGENTS.md入力(最優先ソース)でありながら出力先でもある ためです。README も優先順位を次のように定義しています。

A repository root AGENTS.md (outside .ruler/) if present (highest precedence, prepended)
出典: Ruler README

セットアップと apply

インストールから初期化までは 2 コマンドです。

npm install @intellectronica/ruler
npx ruler init
[ruler] Created /path/to/repo/.ruler/AGENTS.md
[ruler] Created /path/to/repo/.ruler/ruler.toml

.ruler/ruler.toml にはエージェントの有効・無効、出力先の上書き、[gitignore][backup] の設定、MCP サーバー定義がコメントアウトされた状態で並びます。既定は「全エージェント有効・gitignore 更新あり・バックアップあり」です。

検証では、ruler 導入前の状態として次の 2 ファイルを手書きで置いた git リポジトリを用意しました。

printf '# CLAUDE.md\n\nHANDWRITTEN-CLAUDE\n' > CLAUDE.md
printf '# AGENTS.md\n\nHANDWRITTEN-AGENTS\n' > AGENTS.md
git add -A && git commit -m init

そのうえで .ruler/AGENTS.md にチームルールを書き、ruler apply を実行します。

npx ruler apply
[ruler] Applying rules for GitHub Copilot...
[ruler] Applying rules for Claude Code...
[ruler] Applying rules for OpenAI Codex CLI...
(中略・合計 32 エージェント)
[ruler] Applying rules for JetBrains AI Assistant...
[ruler] Updated .gitignore with 48 unique path(s) in the Ruler block.
Ruler apply completed successfully.

Applying rules for の行数を数えると 32 でした。生成された CLAUDE.md は次のようになります。

<!-- Source: AGENTS.md -->

# AGENTS.md

HANDWRITTEN-AGENTS

<!-- Source: .ruler/AGENTS.md -->

# チームルール

- 日本語で応答する
- テストを先に書く

手書きだった # CLAUDE.md ... HANDWRITTEN-CLAUDE は本体から消え、CLAUDE.md.bak にのみ残ります。上書き前にバックアップを作る仕様なので原本は失われませんが、.bak の存在に気づかないまま git add -A するとレビューで驚くことになります。

確認1: ruler --version は ruler のバージョンではない

最初につまずいたのはバージョン確認でした。npm view0.3.44 を返すのに、CLI は別の値を出します。

$ npm view @intellectronica/ruler version
0.3.44
$ npx ruler --version
1.0.0

1.0.0npm init -y が作った 検証用プロジェクト自身 のバージョンです。裏取りとして、プロジェクトの version を書き換えてから再実行しました。

$ node -e "const p=require('./package.json');p.version='9.9.9-test';require('fs').writeFileSync('package.json',JSON.stringify(p,null,2))"
$ npx ruler --version
9.9.9-test

--version が実行元プロジェクトの値をそのまま返しています。導入バージョンを記録したいときは、インストール先の package.json を直接読むのが確実です。

node -p "require('./node_modules/@intellectronica/ruler/package.json').version"
# => 0.3.44

不具合報告や社内の導入記録に「ruler 1.0.0 で発生」と書くと再現できなくなります。バージョンは package.json 側の値で残してください。

確認2: .gitignore/CLAUDE.md が入る

ruler apply.gitignore に専用ブロックを追記します。48 パスの内訳は、生成ファイルとその .bak がセットで並ぶ形です。

# START Ruler Generated Files
/.agent/rules/ruler.md
/.agent/rules/ruler.md.bak
/.aider.conf.yml
/.aider.conf.yml.bak
(中略)
/AGENTS.md
/AGENTS.md.bak
/CLAUDE.md
/CLAUDE.md.bak
/CRUSH.md
/CRUSH.md.bak
/WARP.md
/WARP.md.bak
/firebender.json
/firebender.json.bak
# END Ruler Generated Files

CLAUDE.md を「チームで共有する規約」としてコミットしている場合、この既定は方針とぶつかります。git は追跡済みファイルを .gitignore では除外しないため既存の追跡は続きますが、新規クローンや新規追加の場面で挙動が変わります。ブロックごと生成物として扱うか、ruler.toml[gitignore] を無効化するかを、導入時に決めておく必要があります。

[gitignore]
enabled = false

生成物のうち .gemini/settings.json.qwen/settings.json はこのブロックに含まれておらず、apply 後も未追跡ファイルとして残りました。「生成物は全部 ignore される」前提でコミットすると、この 2 つが混ざります。

確認3: 初回 apply だけ結果が違う

同じソースで ruler apply を 2 回実行すると、出力が変わりました。

実行 AGENTS.md の行数 <!-- Source: ... --> の数
1 回目 17 2(AGENTS.md.ruler/AGENTS.md
2 回目 9 1(.ruler/AGENTS.md のみ)

1 回目は手書きの AGENTS.md が最優先ソースとして前置されるため、その内容が出力にも混ざります。1 回目の実行で AGENTS.md の先頭に <!-- Generated by Ruler --> が付くと、2 回目以降はソース扱いされなくなり、前置分が落ちます。差分は 2 回目に現れます。

 <!-- Generated by Ruler -->
-<!-- Source: AGENTS.md -->
-
-# AGENTS.md
-
-HANDWRITTEN-AGENTS
-
 <!-- Source: .ruler/AGENTS.md -->

3 回目以降は安定するため、実運用では「導入直後の 1 回だけ内容が違う」形になります。CI で ruler apply の差分チェックを入れる場合は、初回コミットの前に 2 回実行して落ち着かせておくと、無用な差分に悩まされません。

確認4: ruler revert で手書き AGENTS.md が消えた

README は revert を次のように説明しています。

The revert command safely undoes all changes made by ruler apply, restoring your project to its pre-ruler state.
出典: Ruler README

この説明のとおりなら、手書きの CLAUDE.mdAGENTS.md はどちらも原本へ戻るはずです。実行結果は次のようになりました。

npx ruler revert
[ruler] Revert completed successfully.
  Files processed: 19
  Files restored from backup: 2
  Generated files removed: 17
  Backup files removed: 2
  Empty directories removed: 5
  .gitignore cleaned: yes
$ cat CLAUDE.md
# CLAUDE.md

HANDWRITTEN-CLAUDE
$ cat AGENTS.md
cat: AGENTS.md: No such file or directory
$ git status --short
 M .gitignore
 D AGENTS.md

CLAUDE.md は原本に戻り、AGENTS.md は削除されました。AGENTS.md.bak も同時に消えているため、git 管理下でなければ手書き内容はこの時点で失われます。空のリポジトリを作り直して apply と revert だけを通す最小手順でも同じ結果になり、2 リポジトリで再現しました。

原因は出力先の共有にあります。32 エージェントに対して出力先は 19 個で、AGENTS.md は複数エージェントが同じパスへ書き込みます。共有パスを 1 件目の処理で .bak から復元し、その後に別エージェントの処理が同じパスを「生成物」として削除すると、最終状態は削除になります。upstream にも .mcp.json の同種不具合(#638 Restore shared MCP configs only once during revert)や Qwen 設定の復元漏れ(#647)が記録されており、共有出力先の revert は既知の弱点です。ただし issue 検索の限りでは、リポジトリ直下 AGENTS.md の削除として報告されたものは見つかりませんでした。

回避策: --keep-backups を付ける

revert には .bak を残すオプションがあります。付けて実行すると結果が変わりました。

npx ruler revert --keep-backups
  Files restored from backup: 18
  Generated files removed: 16
  Backup files removed: 0
  Empty directories removed: 5
$ cat AGENTS.md
A-ORIG
$ cat AGENTS.md.bak
A-ORIG

復元件数が 2 から 18 に増え、AGENTS.md は原本のまま残りました。.bak を消さないことで「復元してから削除する」順序が崩れなくなったと読めます。既定値は --keep-backups が無効(default: false)なので、明示的に付ける運用にしておくのが安全です。

一方で、対象エージェントを切って回避しようとした試みは失敗しました。

[agents.agentsmd]
enabled = false

この設定で apply しても AGENTS.md<!-- Generated by Ruler --> 付きで生成され、revert では同じく削除されました。AGENTS.md を出力先とするエージェントは AgentsMd だけではないため、1 つ無効化しても共有パスへの書き込みは止まりません。

まとめると、実測した回避策の可否は次のとおりです。

手段 結果
ruler revert --keep-backups AGENTS.md が原本のまま復元される(有効)
[agents.agentsmd] enabled = false 生成・削除とも変わらず(無効)
導入前に git commit しておく git checkout -- AGENTS.md で復旧可(回避ではなく保険)

運用への落とし込み

検証結果を踏まえると、導入時に決めておくべき点は 4 つに絞られます。

  1. 導入コミットを先に作る。apply 前の状態をコミットしておけば、共有パスの取りこぼしは git checkout で戻せます。
  2. revert は必ず --keep-backups を付ける。手順書やスクリプトに直接埋め込み、素の ruler revert を打たせないようにします。
  3. .gitignore の方針を明示するCLAUDE.md を規約としてコミットする運用なら [gitignore] enabled = false を選び、生成物として扱うならブロックごと受け入れます。
  4. 初回 apply は 2 回流す。前置ソースの取り込み差分を落ち着かせてからコミットすると、CI の差分チェックが安定します。

いずれも 1 行の設定か 1 行の手順で済みます。ルール重複の解消という本来の効果は大きいので、後片付けの経路だけ先に固めておくのが現実的な使い方です。

まとめ

  • Ruler は 1 ソースを 32 エージェント・19 出力先へ配布し、.gitignore に 48 パスを追記します。効果は明快ですが、CLAUDE.mdAGENTS.md も生成物として扱われます。
  • 既定の ruler revert は、導入前から存在した手書き AGENTS.md を削除しました。README の "restoring your project to its pre-ruler state" と実挙動は一致しません。
  • --keep-backups を付けると復元されます。エージェント単位の無効化では回避できません。
  • ruler --version は実行元プロジェクトのバージョンを返すため、記録には package.json の値を使ってください。

関連記事

参考リンク

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?