指示書がズレていた

「Claude Code と Codex の両方で開発しているので、双方に情報を共有できる仕組みを作れないか。あまり複雑なシステムは嫌だ」という依頼から始まった。
新しい仕組みを作る話だと思って調べ始めたが、結論は逆だった。共有の基盤はすでに揃っていて、壊れていたのは1箇所だけだった。
作る前に何があるかを数える
まずリポジトリの現状を出した。
temp/progress/active.mdとworklog.md— 双方が読み書きする共通の進捗ファイル。専用のスキルもある.claude/skills/と.agents/skills/— スキル定義。両方に同じものを置いているCLAUDE.mdとAGENTS.md— ルールファイル
スキルの同期は以前から気にしていた箇所で、点検スクリプトが用意されていた。走らせたら終了コード0。健全だった。
$ python3 scripts/check-skill-sync.py
adsense-content: OK (179行)
...
issue-inline: 除外(Claude専用) — ...
issue-pipeline: 除外(書き分け) — ...
exit=0
**ここで最初の判断ミスをしている。**スクリプトを走らせる前に diff -rq でディレクトリを比較して「11個中8個が不一致」と報告してしまった。実際の差分は同期を促す注記ブロックで、既に解決済みの問題だった。用意されている点検手段を先に使えば、余計な報告をせずに済んだ。既存の仕組みを探す前に自分で測り始めると、こういうズレ方をする。
残っていた1箇所
diff AGENTS.md CLAUDE.md を取ったら、こちらは本物だった。
Codex には「PRを経由せよ」が届いていなかった。Claude Code には「docker compose run にメモリ上限を明示せよ」が届いていなかった。後者は制限なしで大規模処理を走らせて Docker ごと落とす話で、過去にそれで痛い目を見たから書いたルールのはずだった。それが片方にしか無い。
**乖離が怖いのは、失敗として表に出ないことだ。**テストは落ちないし警告も出ない。「なぜかCodexのときだけmainに直接コミットされる」という形で、ずっと後から気づく。
同期する仕組みを作らなかった
ここで素直に思いつくのは、スキル定義と同じように「複製 + 点検スクリプト」を用意することだった。実際その仕組みはリポジトリにある。
やらなかった。**ルールファイルは1組しかなく、書き分ける理由も無い。**それなら仕組みを増やすより、分かれない形にするほうが安い。
git rm --cached AGENTS.md
rm AGENTS.md
ln -s CLAUDE.md AGENTS.md
Git はシンボリックリンクをモード 120000 で記録する。コミットの差分にもそう出た。
3 files changed, 76 insertions(+), 48 deletions(-)
mode change 100644 => 120000 AGENTS.md
Codex 側の設定はいじっていない。AGENTS.md をファイルシステム経由で開くので、リンクは透過的に解決される。**「Codexに伝える」作業が発生しないのが、この方式を選んだ理由でもある。**配る運用が残っている限り、配り忘れも残る。
依頼者からも「Codexの編集は特にしなくていい? 伝えると言ったら変だけど」と聞かれた。要らない。次のセッションで AGENTS.md を読んだ時点で、統合後のルールが効く。
分割したルールは追随しない
1つ注意が要った。CLAUDE.md から切り出した詳細ルール(コンテキストの読み込み範囲、Bashの出力制限、セキュリティなど)は .claude/rules/ 配下にあり、Claude Code は自動で読むが Codex は読まない。
指示書をリンクで1本にしても、そこから参照している別ファイル群までは揃わない。本体に1行足して埋めた。
- `.claude/rules/` 配下の各ルールは実行主体を問わず適用する。
Claude Code は自動で読み込むが、Codex は自動では読まないため作業前に該当ファイルを確認する。
ルールを読みやすさのために分割するほど、この「自動で読まれる範囲の差」が効いてくる。分割の判断材料に入れておくべきだった。
ホーム配下に置換の残骸があった
リポジトリを直したあと、他の階層も見た。stock-app/frontend/ は CLAUDE.md の中身が @AGENTS.md の1行で、既に同じ実体を指していた。問題なし。
グローバル階層は違った。~/.claude/CLAUDE.md と ~/.codex/AGENTS.md はほぼ同一だったが、Codex側にこれが残っていた。
- `~/.Codex/settings.json`
- プロジェクトの`.Codex/settings.json` / `settings.local.json`
- `.Codex/rules/`配下のすべてのファイル
.claude → .Codex の一括置換が本文にも当たっている。大文字小文字まで巻き込まれていて、.Codex/ などというディレクトリは存在しない。設定変更を禁止するルールが、実在しないパスを禁止対象として並べていた。
同じ置換誤爆は progress スキルでも起きていた記録がある。「CodexとCodexの共通進捗ファイル」という文と、~/.Codex/projects/ という実在しないパスになっていたらしい。コピーして固有名詞を置換する運用は、この形で静かに壊れる。置換後に実在確認をしないと気づけない。
こちらもリンクへ寄せた。ただし対象パスの列挙は、両ツール分を1つのリストへ統合してから寄せる必要がある。リンクにすると「片方だけに当てはまる記述」が置けなくなる。
- `~/.claude/settings.json`、`~/.codex/config.toml`
- `CLAUDE.md` / `AGENTS.md`(グローバル・プロジェクト両方)
残ったもの
正本は2つになった。グローバルの ~/.claude/CLAUDE.md と、リポジトリの CLAUDE.md。どちらも AGENTS.md がリンクとしてぶら下がる。
覚えることは1つで、**ルール変更は CLAUDE.md 側だけを編集する。**リンク側を直接編集すると実体を書き換えることになるので、その注意を本体の冒頭に書いた。
依頼は「共有する仕組みを作れないか」だったが、作ったものはほぼ無い。既にあるものを数えて、壊れている1箇所を分かれない形に直しただけだった。仕組みを足す前に、同じ役割のものが何個あるかを数えるほうが先だと思う。
ちなみに片方に届いていなかったDockerのメモリ制限は、Valscope のデータ取り込みで実際に効くルールだった。2万社分のXBRLを制限なしで回せば止まる。届いていなくて事故らなかったのは運がよかっただけだと思う。
手順としてまとめたものは 指示書を1本にする に置いた。