Codex を組み込むと、最初に壊れるのはモデルではなく channel です
Codex を CLI、SDK、App Server、automation で使い始めると、すぐに「どの入口から実行するか」より面倒な問題が出ます。ユーザー依頼、モデルの分析、shell や MCP の tool 実行、approval request、最終報告、ログ保存が同じ場所へ流れ込み、あとから何を信じればよいか分からなくなる問題です。
たとえば、automation の最終報告に途中の仮説が混ざる。tool のエラーをユーザー向け回答として保存してしまう。approval 待ちを通常のログ行として扱い、UI が止めるべき操作を見逃す。こうした事故は、モデル性能というより harness 側の channel routing 設計不足で起きます。
この記事では、Codex harness を運用するときの channel routing に絞ります。Harness Engineering 全体は Harness Engineering 入門、Codex の接続面は Codex App Server ガイド、sandbox と approval の権限設計は Codex sandbox / approvals 設計ガイド、MCP tool の危険度ヒントは MCP tool annotations 設計ガイド に分けています。
結論は単純です。Codex を業務 harness に入れるなら、message を保存する前に、channel、actor、side effect、review state を分けるべきです。
channel routing は UI の都合ではなく、監査と安全運用の境界
ここでいう channel routing は、Slack やブラウザの通知先を選ぶ話ではありません。Codex の run の中で発生する情報を、性質ごとに分けて扱う設計です。
私は最低限、次の6つに分けます。
| channel | 中身 | 保存先 | そのままユーザーに見せるか |
|---|---|---|---|
| intake | ユーザー依頼、制約、完了条件 | task record | 見せる |
| analysis | 仮説、調査方針、内部判断 | transcript / redacted trace | 原則見せない |
| tool | shell、file、MCP、browser の実行結果 | tool log | 要約して見せる |
| approval | 権限昇格、外部 write、MCP elicitation | approval queue | 判断材料だけ見せる |
| final | 完了報告、検証結果、残リスク | inbox / PR / ticket | 見せる |
| memory | 次回へ残す学び、再発防止メモ | curated memory | 要審査 |
この分類を持たずに、全部を messages として保存すると危険です。特に analysis と final を混ぜると、未検証の仮説が完了報告に見えます。tool と approval を混ぜると、止めるべき操作がただのログになります。memory と transcript を混ぜると、一時的な迷いまで次回の前提になります。
Codex App Server は rich client 向けに conversation history、approvals、streamed agent events を扱う接続面として説明されています。つまり、クライアント側も「イベントが流れてくる」前提で設計する必要があります。単発 API のレスポンスとして扱うと、途中状態の意味を落とします。
まず intake で run の契約を固定する
channel routing の最初の仕事は、ユーザーの依頼をそのまま agent に渡すことではありません。run の契約へ変換することです。
私は intake で次を固定します。
run_idownergoalallowed_filesforbidden_filesallowed_toolsapproval_policyoutput_contractdone_checks
たとえばブログ automation なら、契約はこうなります。
{
"run_id": "blog-2026-08-12-codex-harness-channel-routing",
"goal": "主戦場テーマの記事を1本作りPR化する",
"allowed_files": [
"src/content/blog/*.md",
"data/seo/keyword-plan.md",
"public/llms.txt",
"public/llms-full.txt"
],
"forbidden_files": [".env", "dist/**"],
"approval_policy": "no direct main commit",
"done_checks": ["pnpm seo:update-llms", "pnpm run build"]
}
ここまで切ってから Codex に渡すと、tool 実行や最終報告の意味が安定します。逆に、依頼文だけを投げると、後段の channel で「これは許可済みの変更か」「完了条件は満たしたか」を毎回推測することになります。
analysis は保存しても、成果物には混ぜない
Codex を観察していると、途中の分析は有用です。なぜそのテーマを選んだか、なぜ別案を捨てたか、どのファイルを読んだかが残るからです。ただし、analysis は作業中の仮説であり、成果物ではありません。
私なら、analysis channel は次の扱いにします。
- raw transcript は短期保存にする
- final answer へは検証済みの結論だけを移す
- memory へ残す前に人間か reviewer lane が要約する
- 顧客データ、secret、未公開方針が混ざる run では保存粒度を落とす
これを怠ると、次回 run が「前回の迷い」を事実として読んでしまいます。AIエージェントの memory を便利にするほど、この危険は大きくなります。memory は transcript の別名ではなく、審査済みの運用知識だけを残す場所です。
tool channel は side effect を先に分類する
tool 実行は、見た目が同じログでも危険度が違います。
rg "Codex App Server" src/content/blog
pnpm run build
gh pr create --fill
curl https://example.com/internal-data
これらを同じ tool_output として保存するだけでは足りません。routing 時点で、少なくとも次を付けます。
| 分類 | 例 | routing |
|---|---|---|
| read | rg, sed, git diff | 自動実行し、要約だけ final へ |
| local write | 記事作成、生成ファイル更新 | diff と検証結果を紐づける |
| external read | 公式 docs の検索、GitHub PR 読み取り | source URL と取得時刻を残す |
| external write | PR作成、コメント投稿、deploy | approval と idempotency key を必須にする |
| destructive | delete、reset、広い権限変更 | 原則 block か人間承認へ |
MCP の tool annotations には readOnlyHint、destructiveHint、idempotentHint、openWorldHint のようなヒントがあります。ただし MCP 仕様上、これらは信頼済み分類そのものではありません。harness 側では、server catalog、tool risk tier、approval policy に写してから routing します。
たとえば readOnlyHint: true の tool でも、未承認 server から来たものなら自動許可しません。idempotentHint: true でも、外部 write なら retry key と二重投稿防止を別に持ちます。
approval は tool log ではなく停止点として扱う
Codex の safety controls は、sandbox が何にアクセスできるか、approval policy がいつ止めるかを分けて考えます。ここは channel routing でも同じです。
approval request を普通の event log として保存すると、UI や automation が「止まっている」のか「進んでいる」のか判断できません。approval は独立した停止点として扱います。
{
"approval_id": "apr-20260812-001",
"run_id": "blog-2026-08-12-codex-harness-channel-routing",
"requested_action": "gh pr create",
"side_effect": "external_write",
"scope": "github_pull_request",
"default": "wait",
"expires_at": "2026-08-12T23:00:00+09:00"
}
automation で approval_policy = never を使う場合も、approval channel を消してよいわけではありません。人間へ聞けない lane なら、事前に許された action だけを通し、許可外は fail closed にします。これは「承認を省略する」ではなく、「承認が必要な操作を実行可能範囲から外す」という設計です。
final は作業ログの要約ではなく、受け入れ判断の材料
final channel は、ユーザーに見える最後の文章です。ここに必要なのは、頑張った過程ではなく、受け入れるかどうかを判断する材料です。
私は final answer を次の順に固定します。
- 何を作ったか
- どの検索意図を狙ったか
- 重複チェックで何を除外したか
- どの検証を通したか
- 未解決リスクや review 待ちがあるか
- PR / artifact / next action
この形式なら、inbox、PR本文、Slack通知、監査ログに同じ情報を配っても意味が崩れません。逆に、final に analysis や tool raw output を混ぜると、読む側は何を確認すればよいか分からなくなります。
transcript mirroring は全部保存ではなく、再現に必要な最小単位にする
Codex の config には session transcript の保存設定があります。hooks でも会話を外部ログや memory 作成へ渡せます。便利ですが、ここを雑に mirror すると、保存してはいけない情報まで長期化します。
私は transcript mirroring を3段階に分けます。
| mirror | 保存するもの | 用途 |
|---|---|---|
| raw | 短期の全イベント | 事故調査、debug |
| reviewed | tool call、approval、diff、source | PR review、監査 |
| curated | 次回も使う運用知識 | memory、runbook |
raw transcript は「あとで全部読めば分かる」ための保険です。しかし、通常の運用で見るべきなのは reviewed と curated です。特に external write を伴う run では、raw log より approval_id、tool_call_id、commit_sha、source_url、verification_command を結ぶ方が実務で追えます。
小さく始めるなら4つの routing rule で足りる
最初から複雑な event bus を作る必要はありません。私は小さい team なら、まず次の4ルールだけで始めます。
analysisを final と memory に直通させない- 外部 write は
approvalchannel を必ず通す - tool output は read / write / external / destructive に分類する
- final answer は受け入れ判断の6項目だけに絞る
この4つだけでも、Codex harness はかなり安定します。特に automation、CI、App Server、MCP server を組み合わせるなら、routing がない状態で tool を増やすのは危険です。tool を増やす前に、どの channel へ流し、どこで止め、何を残すかを決める方が、あとからの事故対応が軽くなります。
Codex harness channel routing は、見た目には地味です。ただ、AIエージェントを組織で使うなら、ここが実行品質、監査、承認、memory の土台になります。モデルに賢く動いてもらう前に、harness 側で情報の通り道を設計する。それが、Codex を単発の便利ツールから、再現可能な業務 workflow へ変える一番現実的な手順です。