本文へ移動
🧭

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原則見せない
toolshell、file、MCP、browser の実行結果tool log要約して見せる
approval権限昇格、外部 write、MCP elicitationapproval 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_id
  • owner
  • goal
  • allowed_files
  • forbidden_files
  • allowed_tools
  • approval_policy
  • output_contract
  • done_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
readrg, sed, git diff自動実行し、要約だけ final へ
local write記事作成、生成ファイル更新diff と検証結果を紐づける
external read公式 docs の検索、GitHub PR 読み取りsource URL と取得時刻を残す
external writePR作成、コメント投稿、deployapproval と idempotency key を必須にする
destructivedelete、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 を次の順に固定します。

  1. 何を作ったか
  2. どの検索意図を狙ったか
  3. 重複チェックで何を除外したか
  4. どの検証を通したか
  5. 未解決リスクや review 待ちがあるか
  6. 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
reviewedtool call、approval、diff、sourcePR 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ルールだけで始めます。

  1. analysis を final と memory に直通させない
  2. 外部 write は approval channel を必ず通す
  3. tool output は read / write / external / destructive に分類する
  4. 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 へ変える一番現実的な手順です。

参考資料

WRITTEN BY nidoneko

Full-stack engineer with 8+ years of experience in TypeScript, React, Node.js, and cloud-native development across healthcare, finance, HR, and IoT domains.

View Profile →