Mirai Translate TECH BLOG

株式会社みらい翻訳のテックブログ

Codex notifyフックによるログ運用 〜 ハマったけどソースコード解析して何とかした話

はじめに

みらい翻訳でVPoEをやっているSatieです。

Codex の notify フックで「エージェントとのやりとりログ」を残そうとして、設定や権限周りの落とし穴にハマリ、最終的にCodexのソースコードを生成AIと解析しながら解決しました。

設定の結論から言うと、notify は「トップレベル設定」であり、読み込みタイミングも限定的なので、「思ったように動かない」が起こりやすかったり、CodexのSandbox書き込み権限に関するルールについての特別な設定が必要な箇所がありました。

この記事では、私がハマったポイントと、私のnotify hook構成を短くまとめます。

なお、利用したCodexおよびソースコードのバージョンは0.115.0になります。こちらはCodex-CLI, Codexアプリどちらでも適用可能です。


何をしたかったか(モチベーション)

  • Codex利用時の agent-turn-complete などのイベントをフックし、改善要望・ロール指示のやりとりをログ化して、
  • ログが溜まったら「改善エージェント」に解析させ、指示プロンプトの改善に繋げたい

ハマリポイント 1:notify はトップレベル設定

notify はトップレベルの設定です。誤って別のテーブル配下(例:projects."プロジェクトディレクトリ")に置くと、読み込まれません。

しかも、projects 側に未定義の設定が混ざっていても「エラーで落ちる」ではなく「静かに読み飛ばされて notify が効かない」ため、原因特定が難しくなります。 このことは残念ながら公式ドキュメントには書いていません。

対策

~/.codex/config.toml の設定イメージ

model = "gpt-5.4"
notify = ["python3", "/<YOUR_HOME>/.codex/hooks/notify-dispatch.py"]

[projects."/any/project/path"]
(以下略)
  • notify は必ずトップレベルに置く
  • AI に config.toml を編集させる場合、構造が壊れないようにガードする、もしくは自分で確認する(私は壊されました)

📝 この例は ~/.codex/config.toml に記載して、どのプロジェクトディレクトリでもnotify もフックされます。

プロジェクト個別にnotify イベントを処理したい場合は、 <プロジェクトディレクトリ>/.codex/config.tomlnotify=… を記載しても動作します。


ハマリポイント 2:notify 設定は新セッションでしか読み直されない

notify の設定変更は「新しいセッション開始時」にのみ反映される挙動でした。

設定を直したのに効かないときは、まずセッションを作り直すのが最短です。

codex-cli は、 codex new 、Codexアプリでは 新しいセッションを開始する です。

これもソースコードを解析して分かった仕様です。

📝 セッションやスレッドという呼び方をしていますが、実装上はSessionであり、ログも ~/.codex/sessions 以下に出力されます。

この辺の言葉の揺らぎが少し混乱を助長しますね。


壊れにくい運用:dispatch で受けて、プロジェクトに委譲する

私の例では notify~/.codex/config.toml の設定のため、全プロジェクトに対して同じフックが呼ばれます。

グローバル側で 一次受けをして、プロジェクト側の handler に委譲する構成にしています。デバッグの過程でグローバル側で受けて動作確認する方が都合がよかったのが主な理由ですが、特に理由がなければ、プロジェクト内で設定して、プロジェクト内で直接受けてもよいと思います。

構成案

  • ~/.codex/hooks/notify-dispatch.py(入口)
  • <プロジェクト>/.codex/hooks/notify-handler.py(プロジェクト固有処理)
  • handler が無ければ何もしない

擬似コード例(dispatch)

# ~/.codex/hooks/notify-dispatch.py
import os
import subprocess

project_handler = os.path.join(os.getcwd(), ".codex", "hooks", "notify-handler.py")

if os.path.exists(project_handler):
    subprocess.run(["python3", project_handler], check=False)
else:
    # handler が無ければ何もしない
    pass

ログが書けないときのチェックリスト(権限・サンドボックス)

フックでログを書こうとしても、環境条件で失敗することがあります。OSやCodexで設定されている権限の問題の可能性があります。

そのため、以下を確認します。

1) OS 権限

notify で設定されるスクリプトは Codex を起動したユーザー権限で動作するため、書き込み先に対して OS レベルの権限があるか確認します。

2) Codex の書き込み制約

プロジェクトディレクトリへ書き込むには、少なくとも以下を満たす必要があります。

  • プロジェクトディレクトリが trusted であること
  • sandbox-moderead-only ではない(書き込みたい場合は workspace-write を推奨)
  • ただし、.git / .codex / .agents 配下は原則書き込み対象外

.gitについての情報はありましたが、それ以外はソースコードを解析して判明しました。

~/.codex/config.toml の設定確認場所

model = "gpt-5.4"
notify = ["python3", "/<YOUR_HOME>/.codex/hooks/notify-dispatch.py"]

[projects."/any/project/path"]
trust_level = "trusted"

<プロジェクトディレクトリ>/.codex/config.toml の設定確認場所

sandbox_mode = "workspace-write"

書込対象外ディレクトリに例外的に書き込む場合(writable_roots)

設定で sandbox_workspace_write.writable_roots に列挙します。

<プロジェクトディレクトリ>/.codex/config.toml の設定確認場所

sandbox_mode = "workspace-write"

[sandbox_workspace_write]
writable_roots = ["./logs", "./user-worklog"]

パスは絶対パス、または設定ファイル(<プロジェクトディレクトリ>/.codex/config.toml )からの相対パスです。


調査の進め方:ソースコード解析が効いた

公式ドキュメントや Issue、Web 記事だけでは情報が古かったり・誤っていたり・不足していたりして、解決できませんでした。

最終的には、生成AI にソースコードを読ませつつ「調査ポイントを指示 → 解説してもらう」のが最短でした。

(調査結果が挙動とズレることもあったので、その場合は前提を疑って再調査させるのが重要です)


まとめ

  • notify はトップレベル設定。 [projects.”プロジェクトディレクトリ”] などの別テーブル 配下に置くと効かない
  • notify 設定変更はセッション開始時に反映される
  • dispatch → project handler で委譲する構成を紹介したが、プロジェクト個別でも問題ない
  • ログが書けないときは権限と sandbox 制約( sandbox_workspace_write.writable_roots )を確認
  • ハマってWeb検索中心の調査で解決できなかったら、生成AI×ソースコード解析が強い