Skip to content

meta(codex): changelog_advisory hook が「チェッカが動かなかった」を「フラグメント違反」と同じ exit 2 で編集ブロックする #947

Description

@rizumita

Problem

.codex/hooks/changelog_advisory.py(28行、PostToolUse / apply_patch|Edit|Write)は
tools/aggregate_changelog.sh check を実行し、戻り値が 0 以外なら一律 exit 2 を返す。

result = subprocess.run([str(root / "tools" / "aggregate_changelog.sh"), "check"], ...)
if result.returncode == 0:
    return 0
sys.stderr.write(result.stderr or result.stdout)
return 2

Codex hook の exit 2 は編集のブロックである。したがって次の2つが区別されない:

  1. チェッカが動いて「フラグメントが無い」と判定した(正当な advisory 所見)
  2. チェッカがそもそも動かなかった(環境の失敗)

tools/aggregate_changelog.sh:2
(( BASH_VERSINFO[0] >= 4 )) || { echo "aggregate_changelog.sh requires Bash 4 or newer" >&2; exit 1; }
で始まる。macOS 既定の bash は 3.2 なので、シェバン #!/usr/bin/env bash
homebrew の bash を拾えなかった場合、スクリプトは内容を一切見ずに exit 1 する。
それがそのまま exit 2 になり、編集がブロックされる。

実測(2026-08-28)

Codex エージェントの編集が次の表示でブロックされた:

• Edited rust/fslc/src/main.rs (+1 -1)
• PostToolUse hook (blocked)
  feedback: aggregate_changelog.sh requires Bash 4 or newer

編集内容は changelog と無関係(Rust の関数呼び出し1行)。フラグメントの状態も適切だった。
にもかかわらず「フラグメント違反」と同じ経路でブロックされ、
エージェントに返ったメッセージは requires Bash 4 or newer だけである。
これは「あなたの編集に問題がある」とも「フックが動かなかった」とも読めない。

なぜ単なる環境問題ではないか

このフックの判定は ambient state で変わる。 同一セッション中、同じ種類の編集が
通ることもブロックされることもあった。subprocess.run はスクリプトを直接実行するので、
シェバンが解決する bash はフックプロセスが継承した PATH 次第である。
homebrew の bash が PATH にあれば通り、無ければブロックされる。

AGENTS.md の不変条件はこの形を明示的に否定している:
「A control whose verdict depends on ambient state is not a control.」
このフックはまさにそれで、しかも編集をブロックする側にある。

名前が changelog_advisory で docstring も
"Return canonical changelog-checker feedback" と述べているのに、
実際には advisory ではなくゲートとして働いている点も、宣言と執行の不一致である。

提案

1. 「動かなかった」と「違反を見つけた」を分ける。
aggregate_changelog.sh が起動不能(Bash バージョン不足など)なら、
それは advisory を提供できなかったということなので、編集をブロックしない。
stderr に理由を出して exit 0 を返す。違反を見つけたときだけ exit 2 にする。

区別の手段としては、aggregate_changelog.sh
「実行不能」に専用の終了コード(例 3)を使うのが最も明示的。
現行の exit 1 は「違反あり」と共用されている。

2. bash 4 を明示的に探す。
#!/usr/bin/env bash に頼らず、フック側で bash 4+ の実体を解決してから渡す
(/opt/homebrew/bin/bash などを候補に、BASH_VERSINFO を確認)。
見つからなければ 1 の経路で「advisory を提供できなかった」として通す。

3. 名前と挙動を一致させる。
ブロックする設計を維持するなら advisory という名前を変える。
advisory を維持するなら exit 2 を使わない。どちらでもよいが、両方ではいけない。

受け入れ条件

  • aggregate_changelog.sh が起動不能な環境で、changelog と無関係な編集がブロックされない
  • フラグメントが本当に足りない編集は依然ブロックされる(正の対照)
  • 上記2つを別々の変異で実測し、期待と produced を並べる
    (起動不能を再現するには PATH から bash 4 を外す)
  • エージェントに返るメッセージが、ブロックの理由と「何をすれば通るか」を述べている
  • フックの名前と挙動が一致している
  • 判定が PATH に依存しない、または依存する場合は依存しない側に倒れる

参考

.codex/hooks/hooks.json の PreToolUse には cargo_pre_tool_use.py があり、
これが cargo コマンドを cargo_lock.py で自動的に包む。その自動包摂が
明示呼び出しと衝突して自己デッドロックする件は #946

Refs #922, #924, #929, #946.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions