fix(adapter): keep the webhook handling re-arm in every delivery mode [hooks, tests, docs] - #1802
Conversation
… [hooks, tests, docs] `LI_PLUS_WEBHOOK_DELIVERY` は「誰が MCP ツールを呼ぶか」だけを切り替える設定であり、届いたイベントをどう扱うかの面は切り替えない。しかし 3 port すべての条件式が webhook ブロック全体を落としていたため、`channel` / `mcp_hook` では取り扱い指示(前景報告フィルタ)も一緒に消えていた。2026-08-24 のセッションで 9 件の pending event が turn 1 に注入されながら inspect も `mark_processed` も走らず、約 15 tool call 後に別手続きが名指しするまで放置された観測がこれである。 修理: - `adapter/claude/hooks/on-user-prompt.sh` / `adapter/codex/hooks/on-user-prompt.sh` / `adapter/codex/hooks/on-user-prompt.ps1` の条件分岐を 2 分岐化した。呼び出し半分(`get_pending_status` を AI 自身に呼ばせる行)だけを poll 限定に残し、取り扱い半分は全モード無条件にした。呼び出し半分を `channel` / `mcp_hook` へ戻していないので、#1632 F7 の二重配送抑止はそのまま効く。 - `mark_processed` の re-arm は poll ブランチにも無かった(`rules/operations/operations.md` Operations Rules が mandatory と述べている語が、3 モードのどの hook 面にも現れていなかった)ため同時に追加した。 - 正本は hook へ写していない。Trigger Check Gate re-arm の `Axis detail:` と同じ形で `rules/operations/main-agent-procedures.md` Foreground webhook notification intake を指すポインタ行を置いた。 always-on 常駐に任せられない理由は先行判断と同型である。当該正本は発火時刻を `each user turn start` と自ら述べており、常駐はロード保証であって発火保証ではない。ターン境界を発火できる面は hook だけであり、`rules/model/trigger-check-gate.md` Trigger firing が 5-axis Gate を同じ理由で決定論的 hook re-arm へ移した判断をここでも適用している。 `docs/5.-Notifications.md` は変更していない。「transport は polling でも push でもよい。前景一致判定……の規則は transport によって変えない」は既に正しく、破れていたのは adapter 側だけである。 tests/test_on_user_prompt_webhook_rearm.py: 3 port × 5 config 状態(poll / key 不在 / config 不在 / channel / mcp_hook)で、取り扱い半分の生存、呼び出し半分の poll 限定、正本ポインタの存在と re-arm の行数上限、3 port の厳密一致を検証する。変更前の hook に対して 37 件失敗することを確認済み。 release type = patch(配信モード設定の意味論は変わらず、L6 adapter の実装欠落の修理である)。 #1798
brake 1 — parallel subagent eval (N=3, M=all axes, P=1)対象 SHA =
所見 1 — 軸5(正本ポインタが主張内容を運んでいるか)/ 3体中3体3体が独立に同一の literal へ到達した。re-arm が 3 port から emit する行:
指されている節(
もう一方のイベント類について同節が述べているのは:
すなわち re-arm が主張する2項のうち、 3体とも「ポインタだけを辿った読み手は、主張の一般側について根拠に到達しない」と述べている。1体は 所見 2 — 固定軸(impression-literal 検出)/ 3体中1体1体が2箇所を Detection sign (A)(規則がどう書かれるに至ったかを語る文が、いま何を要求するかの代わりに置かれている)として flag した。
removal test の根拠として、いずれも直前の文が要件を機構として述べ切っている( 残り2体は clean と判定。両者とも同じ #1798 引用文を読んだうえで、要件は先行文に在り引用はそれを置き換えるのではなく後続しているため、Negative の保護カテゴリ(既知の誤読・回帰を防ぐ explanatory rationale)に当たると判定している。 同体は対照として、 掃引規模は3体とも一致: 所見の無い軸
|
…pointer [hooks, tests] brake 1(N=3 / M=全軸 / P=1 @ c7d8ac8)の統合コメントに対する著者裁定。各所見の採否と理由を以下に記録する。 --- 所見1(軸5、ポインタが主張内容を運んでいるか、3/3)— ACCEPT --- ACCEPT。3 port の re-arm ポインタ行に一般義務の在処 `rules/operations/operations.md` Operations Rules を追加し、`tests/test_on_user_prompt_webhook_rearm.py` の正本ポインタ表明を `CANONICAL_TOKENS` の2件へ強化した。 --- 所見2(固定軸 impression-literal、1/3)— 精製しない --- `skills/evolution-impression-literal-detection/SKILL.md` Aggregation により 1-of-3 は auto-refine 対象外であり、精製閾値(2 以上)に達していないことを理由として精製しない。同 spec が要求するとおり、flag された literal を以下に記録する。 - `adapter/claude/hooks/on-user-prompt.sh:26-28` — `Suppressing it alongside the call half is #1798: events arrived and no surface said what to do with them.` - `adapter/claude/hooks-settings.md:190-191` — `Dropping both halves together is #1798 — events were delivered into context and no surface said what to do with them.` 両 literal が named SHA に当該 path:line で実在することは確認済み。この軸では count が判定であるため、私自身の当否見解は判定入力にならない。 --- 所見2 の divergence pair(Procedure step 6 / Design Dimensions, Divergence handling)--- 固定軸の 1-of-3 は split であるため、判定を書く前に2問を1回ずつ実施した。 問1 same-question check = yes。3体は held prompt literal(同一文言)を受け取り、同一の phrase 集合に対して同一の問いに答えている。flag 側と clean 側は同じ phrase へ反対の判定を出しており、別々の読みへすれ違ったのではない。 問2 why-diverged = 明確な criterion の適用ばらつきであり、criteria の曖昧性ではない。Negative リストの当該項は `impression literal only when removal leaves both the behavior semantic and the revision stability unchanged.` と2節を明示し、Detection signs (A) の単位は `in place of what it now requires` と述べている。clean 側2体はこの2節と `in place of` を当てて保護側に置いた。flag 側は1節版の removal test のみを当てている。criterion は既に両点を述べており不足していない。 よって divergence は「同一の問いに答え、criteria は足りていた」で閉じる。criteria gap の観測は起票しない。step が求めるから gap を書き起こすのは押し付け余剰であり、当該 spec 自身が失敗様態として名指している。 --- 所見の無い軸(軸1-軸4)--- いずれも 3/3 clean。適用する変更は無い。 検証: 修正前のポインタに対して新表明が 15 件失敗すること(3 port × 5 config 状態)を確認済み。フルスイート 78 tests OK。 release type = patch(ポインタの解決先追加であり、user/system observable な挙動変化を持たない)。 #1798
smileygames
left a comment
There was a problem hiding this comment.
自己レビュー(親、semi_auto)
受入条件の照合 — pass。
issue #1798 の目的(channel / mcp_hook で webhook の取り扱い指示が毎ターン面から落ちる経路を塞ぐ)を満たしている。制約もすべて守られている:
- 呼び出し半分を
channel/mcp_hookへ戻していない。 #1632 F7 の二重配送抑止はそのまま効く。brake 1 軸1 が 3 port × 5 状態の 15 組すべてを実行して確認し、3体とも 3/3 clean。 - 3 port 同時変更。 軸2 が emit テキストの 3 port 一致を確認、3/3 clean。
- 正本を hook へ写していない。 re-arm は 4 行のまま、ポインタ形式。
adapter/claude/hooks-settings.md/adapter/codex/hooks-config.mdおよび docs が追従(軸4、3/3 clean)。
本 issue はこのセッション自身の観測から起票したものである。ターン1で 9 pending GitHub webhook events が注入されながら intake が走らず、PR #1763 の due check がその手続きを名指すまで約15 tool call 走り続けた。PR #1763 の観測エントリが予告していた2方向のうち「relocation が挙動を失う」側が発火した形であり、本 PR はその根本(hook 条件分岐が2つの半分をまとめて落とす)を塞ぐ。
brake 1
N=3 / M=all axes / P=1、単一ラウンド。所見は PR 上の集約コメント(#issuecomment-5396103930)。軸1-軸4 が 3/3 clean、軸5 が 3/3 所見、固定軸が 1/3 所見。
著者の裁定と、親による検証
- 所見1(軸5、ポインタ解決、3/3)— accept。 著者は 3 port の
Intake detail:行へ一般義務の在処rules/operations/operations.mdOperations Rules を追加した。親が 3 port すべてを実読して同一文字列であることを確認。指先の実在も確認済み(rules/operations/operations.md:91のmark_processed is mandatory for every consumed webhook event.)。テスト側はCANONICAL_TOKENSが2件を pin する形へ強化されており、修正前ポインタに対して 15 件(3 port × 5 状態)失敗する非空虚性の主張と整合する。 - 所見2(固定軸 1/3)— 精製せず記録。
skills/evolution-impression-literal-detection/SKILL.mdAggregation の1 of N=3 flags → do not auto-refine; record the flagged literal in the commit body ... naming the below-threshold count as the reasonに一致する。commit501aaf2の body に2件の literal が verbatim で、閾値未達を理由として記録されていることを実読で確認した。
訂正ラウンドは0回。却下として残った所見は無い(軸5 は accept、固定軸は閾値未達の記録扱いであり却下ではない)。
divergence pair の検証 — ここで親側の誤りを1件訂正した
固定軸が 1/3 で割れたため著者が2問を実施し、same-question = yes、why-diverged = 「明確な criterion の適用ばらつき」と結論し、根拠として Negative リストの2節形(impression literal only when removal leaves both the behavior semantic and the revision stability unchanged.)を引用した。
これは PR #1801 の著者が同じ spec について述べた診断(prompt literal は "each phrase"、Negative カテゴリは block 規模で書かれている、という unit 不一致)と食い違う。2体が同一ファイルを別様に述べているため、親が skills/evolution-impression-literal-detection/SKILL.md を実読して裁定した。
結果:本 PR の著者の引用が正しい。 Negative の当該項は2節形を明示しており、block 規模という記述は同ファイルのどこにも無い。#1801 の著者の診断は literal に支持されない。
親はその診断を、spec を開かないまま memory/promotion_tally.md へクラスタとして書き込んでいた。実読後に訂正済みで、実在する不整合は別のもの——Prompt literal と本文 Test 行がいずれも1節の removal test を述べ、Negative の当該項だけが2節を要求する——として書き直した。訂正の経緯はクラスタ内と memory/self-evaluation_log.md に記録した。本 PR のマージ判断には影響しない(固定軸は閾値で処理されており、割れの診断はゲートになっていない)。
実行条件のうち仕様と異なる点(記録)
Procedure step 2 の operational copy は適用していない。対象は hook script body / adapter docs / docs/ / tests/ であり、いずれも subagent context へ自動注入される集合に入らない(skills/evolution-parallel-agent-eval/SKILL.md Constraint: Character_Instance non-inheritance)。step 2 の条件が成立しないため step 5 の restore も不要であり実施していない。
brake 2
非該当。変更8ファイルに layer: L1-model を宣言するものは無い(hook 3本は frontmatter を持たない script、adapter docs 2本・docs/ 2本・tests/ 1本はいずれも L1 ではない)。
未処理として残す事項(commit body に載る面が無いためここに記録)
著者が第1報で計測した case-sensitivity の port 間分裂——LI_PLUS_WEBHOOK_DELIVERY=Mcp_Hook で bash 2 port は呼び出し半分を emit し PowerShell port は抑止する——は本 PR で未処理であり、比較演算子は変更されていない(親が差分を実読して確認済み)。brake 1 軸1 は canonical case の5状態のみを掃いており、この面には届かない。
既に issue 化済み:#1804。 著者の第2報より前に親が起票しており、そこに実測(同一ホスト上で 'Mcp_Hook' -ne 'mcp_hook' = False、[ "Mcp_Hook" != "mcp_hook" ] = true)と、他の config 値も同じクラスかを先に掃引せよという制約を記載してある。本 PR で扱わないことは正しい。
同じく本セッションで起票済み:#1803(worktree をまたいで共有される git stash が並列 subagent の未コミット作業を静かに入れ替える)。本 PR と #1801 の実装者が両側から独立に報告した事象で、最終状態は親が実測し、失われたものは無いことを確認している。
マージ状態
origin/main が #1799 と #1801 で進んだため BEHIND になっていた。gh pr update-branch で merge により追従(rebase + force push は使わない。force push は rules/operations/main-agent-procedures.md Human confirmation required の人間ゲート対象であり、squash マージで消える追従に消費する理由が無い)。追従後 CI green(governance ×2 pass)、mergeStateStatus = CLEAN。
リリース種別
patch。 rules/operations/release-version-rule.md に照らし、L6 adapter の実装欠落の修理であって配信モード設定の意味論は変わらず、user/system observable な挙動変化を持たない。semi_auto の patch は人間ゲート無しで AI 直接マージ。L1 brake 2 override は非該当。
次段
squash マージへ進む。マージ後、観測エントリを起票し、パッチリリースと wiki sync へ進む。
Closes #1798
LI_PLUS_WEBHOOK_DELIVERYは「誰が MCP ツールを呼ぶか」だけを切り替える設定であり、届いたイベントをどう扱うかの面は切り替えない。しかし 3 port すべての条件式が webhook ブロック全体を落としていたため、channel/mcp_hookでは取り扱い指示も一緒に消えていた。本 PR は条件分岐を 2 分岐化し、呼び出し半分だけをpoll限定に残す。変更内容
hook 3 port(parity 同時変更)
adapter/claude/hooks/on-user-prompt.sh/adapter/codex/hooks/on-user-prompt.sh/adapter/codex/hooks/on-user-prompt.ps1get_pending_statusを AI 自身に呼ばせる行)=poll/ 未設定のみ。channel/mcp_hookへ戻していないので Fix adapter hook defects: matcher field never resolves, dead promotion detectors, errexit leak, port parity gaps #1632 F7 の二重配送抑止はそのまま効く。mark_processed)= 全モード無条件。mark_processedの re-arm はpollブランチにも無かったため同時に追加した。rules/operations/operations.mdOperations Rules が mandatory と述べている語が、3 モードのどの hook 面にも現れていなかった。Axis detail:と同じ形でrules/operations/main-agent-procedures.mdForeground webhook notification intake を指すポインタ行を置いた。追従した記述
adapter/claude/hooks-settings.md(mcp_tool entry behavior 節)adapter/codex/hooks-config.md(mcp_tool webhook entry 節)docs/6.-Adapter.md(### on-user-prompt.sh節とツリー注記)docs/B.-Configuration.md(モード表と注意書き)契約テスト
tests/test_on_user_prompt_webhook_rearm.pypoll/ key 不在 / config 不在 /channel/mcp_hook)で、取り扱い半分の生存、呼び出し半分のpoll限定、正本ポインタの存在と re-arm の行数上限、3 port の厳密一致を検証する。判断の根拠
always-on 常駐に任せられない理由は先行判断と同型である。正本自身が発火時刻を
each user turn startと述べており、常駐はロード保証であって発火保証ではない。ターン境界を発火できる面は hook だけであり、rules/model/trigger-check-gate.mdTrigger firing が 5-axis Gate を同じ理由で決定論的 hook re-arm へ移した判断をここでも適用している。docs/5.-Notifications.mdは変更していない。「transport は polling でも push でもよい。前景一致判定……の規則は transport によって変えない」は既に正しく、破れていたのは adapter 側だけである。release type
patch — 配信モード設定の意味論は変わらず、L6 adapter の実装欠落の修理である。user/system observable な仕様変更を持たない。