Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions adapter/claude/hooks-settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ Target: `{workspace_root}/.claude/settings.json`
Real files, copied verbatim into `{workspace_root}/.claude/hooks/` on bootstrap
(with `{LI_PLUS_TAG}` placeholder replaced by the resolved target tag):

- `adapter/claude/hooks/on-user-prompt.sh` — per-turn Trigger Check Gate re-arm + webhook check (Character_Instance is loaded via output-styles, not per-turn re-notify)
- `adapter/claude/hooks/on-user-prompt.sh` — per-turn Trigger Check Gate re-arm + webhook re-arm (the call half is `poll`-only; the handling half is emitted in every delivery mode — see the mcp_tool entry behavior section below). Character_Instance is loaded via output-styles, not per-turn re-notify
- `adapter/claude/hooks/on-session-start.sh` — Cold-start Synthesis material emitter (matcher-aware: `startup` runs diff-only against `{workspace_root}/.claude/state/last-cold-start-emit.json`; `resume` / `clear` / `compact` / `fork` re-anchor only the cold-start rule anchor — see `rules/evolution/cold-start-synthesis.md` for the emission-state table)

All five documented SessionStart matchers are registered. An unregistered
Expand Down Expand Up @@ -174,10 +174,21 @@ Preconditions for the entry to actually deliver webhook context to the AI:
side, so the wrapped output reaches the AI prompt context.

`Li+config.md`'s `LI_PLUS_WEBHOOK_DELIVERY` setting controls the *bash hook's*
reminder text behavior (poll / channel / mcp_hook) independently. The
`mcp_tool` entry itself fires unconditionally; setting
`LI_PLUS_WEBHOOK_DELIVERY=mcp_hook` suppresses the bash hook's reminder text so
the wrap delivery is the single source of webhook context.
call half (poll / channel / mcp_hook) independently. The `mcp_tool` entry itself
fires unconditionally; setting `LI_PLUS_WEBHOOK_DELIVERY=mcp_hook` suppresses the
bash hook's "call the tool yourself" line, so the wrap delivery is the single
source of webhook context.

It does not suppress the handling half. The webhook block carries two separable
things — who calls the tool, and what to do with what arrives — and the delivery
mode selects only the first. `channel` and `mcp_hook` both replace the call and
neither replaces the handling, so the report filter and the `mark_processed`
re-arm are emitted in every mode. They have to be emitted by the hook rather than
left to the always-on canonical
(`rules/operations/main-agent-procedures.md` Foreground webhook notification
intake): its firing moment is `each user turn start`, and residency is a load
guarantee, not a firing one. Dropping both halves together is #1798 — events were
delivered into context and no surface said what to do with them.

If `github-webhook-mcp` is **not connected**: Claude Code's mcp_tool resolver
returns a `not connected` error per turn. The error is surfaced as plain text
Expand Down
34 changes: 24 additions & 10 deletions adapter/claude/hooks/on-user-prompt.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,24 @@
export PATH="$HOME/.local/bin:$PATH"
PROJECT_ROOT="${CLAUDE_PROJECT_DIR:-.}"

# --- Webhook notification reminder ---
# Read LI_PLUS_WEBHOOK_DELIVERY from Li+config.md
# poll (default / unset) = emit reminder text so Claude calls the MCP tool
# channel = MCP channel delivers events directly; skip reminder
# mcp_hook = a sibling type=mcp_tool hook entry invokes the MCP
# tool directly without going through Claude; skip reminder
# --- Webhook notification re-arm ---
# The block carries two separable halves, and LI_PLUS_WEBHOOK_DELIVERY selects
# only the first: who calls the tool, never who handles what arrives.
# call half = "Run ... get_pending_status silently", emitted under poll
# (default / unset) alone. channel delivers events over the MCP
# channel and mcp_hook has a sibling type=mcp_tool entry invoke
# the tool directly, so both already replace this half; emitting
# it there is the double delivery described below.
# handling half = the report filter and mark_processed. Nothing replaces it in
# any mode. Its firing moment is `each user turn start`
# (rules/operations/main-agent-procedures.md Foreground webhook
# notification intake), and a per-turn hook is the only surface
# that can fire a turn boundary - always-on residency is a load
# guarantee, not a firing one. Suppressing it alongside the call
# half is #1798: events arrived and no surface said what to do
# with them.
# The re-arm stays terse and points at the canonical instead of copying it, the
# same shape the Trigger Check Gate re-arm below uses.
#
# `tr -d '\r'` normalises a CRLF-saved Li+config.md, matching both codex ports
# (.sh does the same, .ps1 uses .Trim()). Without it the extracted value is
Expand All @@ -25,13 +37,15 @@ PROJECT_ROOT="${CLAUDE_PROJECT_DIR:-.}"
# Latent rather than live: on a Windows/Git-Bash host both gawk and MSYS command
# substitution drop the CR on their own, so the miss needs a POSIX host.
WEBHOOK_DELIVERY=$(awk -F= '/^LI_PLUS_WEBHOOK_DELIVERY=/{print $2}' "$PROJECT_ROOT/Li+config.md" 2>/dev/null | tr -d '\r')
echo ""
echo "━━━ Webhook: check pending notifications ━━━"
if [ "$WEBHOOK_DELIVERY" != "channel" ] && [ "$WEBHOOK_DELIVERY" != "mcp_hook" ]; then
echo ""
echo "━━━ Webhook: check pending notifications ━━━"
echo "Run mcp__github-webhook-mcp__get_pending_status silently."
echo "Report only foreground-relevant or notable items."
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
fi
echo "Report only foreground-relevant or notable items."
echo "mark_processed every consumed event; own-operation arrivals promptly."
echo "Intake detail: rules/operations/main-agent-procedures.md Foreground webhook notification intake; mark_processed mandate: rules/operations/operations.md Operations Rules (both always-on)."
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

# --- Trigger Check Gate re-arm (every turn) ---
echo ""
Expand Down
14 changes: 9 additions & 5 deletions adapter/codex/hooks-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,9 @@ is `.ps1` (Windows native, primary on the verified Codex Windows env) + `.sh`
On `resume` / `clear` / `compact`: rules re-injection + language contract marker
+ cold-start anchor only.
- `adapter/codex/hooks/on-user-prompt.{ps1,sh}` — per-turn Trigger Check Gate
re-arm + webhook reminder (Character_Instance lives in AGENTS.md, not re-notified
per turn).
re-arm + webhook re-arm, whose call half is `poll`-only and whose handling half
is emitted in every delivery mode (Character_Instance lives in AGENTS.md, not
re-notified per turn).
- `adapter/codex/hooks/post-tool-use.{ps1,sh}` — sub-issue refs auto-append on
`gh pr create`.

Expand Down Expand Up @@ -237,7 +238,10 @@ tag-tracking anchor. Bootstrap's tag-mismatch check reads this line.
The Claude template adds a sibling `type: "mcp_tool"` UserPromptSubmit entry that
calls `get_pending_status` on `github-webhook-mcp`. The Codex hooks schema documents
only `type: "command"` handlers. Therefore the Codex webhook intake stays on the
**poll** path: the `on-user-prompt` hook emits the reminder text and the AI calls
the MCP tool itself. `LI_PLUS_WEBHOOK_DELIVERY=channel` / `mcp_hook` suppress the
reminder, but a Codex host without an mcp_tool hook substrate falls back to `poll`
**poll** path: the `on-user-prompt` hook emits the call line and the AI calls the
MCP tool itself. `LI_PLUS_WEBHOOK_DELIVERY=channel` / `mcp_hook` suppress that call
half only — the handling half (report filter + `mark_processed`) is emitted in every
mode, because nothing in either mode replaces it and its firing moment is `each user
turn start`, which only a per-turn hook can fire (#1798). A Codex host without an
mcp_tool hook substrate falls back to `poll`
(see `adapter/codex/AGENTS.md` Optional Webhook Notification Flow).
15 changes: 10 additions & 5 deletions adapter/codex/hooks/on-user-prompt.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
#
# Webhook delivery: the Claude version emits a reminder so the AI calls the
# github-webhook-mcp tool. The poll/channel/mcp_hook switch is read from
# Li+config.md LI_PLUS_WEBHOOK_DELIVERY (default = poll = emit reminder).
# Li+config.md LI_PLUS_WEBHOOK_DELIVERY (default = poll) and gates the call half
# of the webhook block only; the handling half is emitted in every mode.
$ErrorActionPreference = 'SilentlyContinue'

# Read stdin payload (Codex passes JSON: session_id, cwd, hook_event_name, ...).
Expand All @@ -38,13 +39,17 @@ if (Test-Path -LiteralPath $configFile) {

$sb = [System.Text.StringBuilder]::new()

# The call half is poll-only; the handling half is unconditional. Rationale
# and the #1798 failure it repairs are in the claude port this one mirrors.
[void]$sb.AppendLine('')
[void]$sb.AppendLine('━━━ Webhook: check pending notifications ━━━')
if ($webhookDelivery -ne 'channel' -and $webhookDelivery -ne 'mcp_hook') {
[void]$sb.AppendLine('')
[void]$sb.AppendLine('━━━ Webhook: check pending notifications ━━━')
[void]$sb.AppendLine('Run mcp__github-webhook-mcp__get_pending_status silently.')
[void]$sb.AppendLine('Report only foreground-relevant or notable items.')
[void]$sb.AppendLine('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')
}
[void]$sb.AppendLine('Report only foreground-relevant or notable items.')
[void]$sb.AppendLine('mark_processed every consumed event; own-operation arrivals promptly.')
[void]$sb.AppendLine('Intake detail: rules/operations/main-agent-procedures.md Foreground webhook notification intake; mark_processed mandate: rules/operations/operations.md Operations Rules (both always-on).')
[void]$sb.AppendLine('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')

# --- Trigger Check Gate re-arm (every turn) ---
[void]$sb.AppendLine('')
Expand Down
12 changes: 8 additions & 4 deletions adapter/codex/hooks/on-user-prompt.sh
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,17 @@ CONTEXT=""
append() { CONTEXT="${CONTEXT}$1
"; }

# The call half is poll-only; the handling half is unconditional. Rationale
# and the #1798 failure it repairs are in the claude port this one mirrors.
append ""
append "━━━ Webhook: check pending notifications ━━━"
if [ "$WEBHOOK_DELIVERY" != "channel" ] && [ "$WEBHOOK_DELIVERY" != "mcp_hook" ]; then
append ""
append "━━━ Webhook: check pending notifications ━━━"
append "Run mcp__github-webhook-mcp__get_pending_status silently."
append "Report only foreground-relevant or notable items."
append "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
fi
append "Report only foreground-relevant or notable items."
append "mark_processed every consumed event; own-operation arrivals promptly."
append "Intake detail: rules/operations/main-agent-procedures.md Foreground webhook notification intake; mark_processed mandate: rules/operations/operations.md Operations Rules (both always-on)."
append "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

# --- Trigger Check Gate re-arm (every turn) ---
append ""
Expand Down
14 changes: 7 additions & 7 deletions docs/6.-Adapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,14 +135,14 @@ self-evolution observation surface も同じく比較セット外だが、外す

トリガー:`UserPromptSubmit` — ユーザーがメッセージを送信するたび(Claude の処理開始前)。

動作:通知取り込みリマインダーのみ。Character Instance は output-styles 機構で system prompt に常駐するため、毎ターン hook 再通知は不要(v1.16.11 で撤廃)。
動作:Trigger Check Gate の毎ターン re-arm と、webhook 取り込みの毎ターン re-arm。Character Instance は output-styles 機構で system prompt に常駐するため、毎ターン hook 再通知は不要(v1.16.11 で撤廃)。

通知取り込みリマインダー:Li+config.md の `LI_PLUS_WEBHOOK_DELIVERY` を読み、配信モードに応じて挙動を切り替える。
- 未設定 / `poll`:リマインダーテキストを stdout へ出力し、AI に MCP ツールを呼び出させる(既定、後方互換)
- `channel`:MCP channel がリアルタイム配信を担うためリマインダーをスキップする
- `mcp_hook`:別途 `UserPromptSubmit` に追加された `type: "mcp_tool"` hook が MCP ツールを直接呼び出すためリマインダーをスキップする(settings.json template の既定)
webhook 取り込み re-arm は**分離可能な 2 つ**を運ぶ。Li+config.md の `LI_PLUS_WEBHOOK_DELIVERY` が切り替えるのは下の**呼び出し半分**だけであり、届いたものをどう扱うかの面は切り替えない。

関連性判定と destructive consume の正本は [5. Notifications](5.-Notifications) に従う。
- **呼び出し半分**(未設定 / `poll` のみ):`mcp__github-webhook-mcp__get_pending_status` を AI 自身に呼ばせる指示を stdout へ出力する。`channel` は MCP channel が、`mcp_hook` は `UserPromptSubmit` の `type: "mcp_tool"` hook(settings.json template の既定)が同じ呼び出しを代替するため、両モードではこの半分を出さない。出せば二重配送になる。
- **取り扱い半分**(全モード):前景報告フィルタと `mark_processed` の re-arm。これを代替するものはどのモードにも無い。発火時刻は `each user turn start` であり、always-on 常駐はロード保証であって発火保証ではないため、ターン境界を発火できる hook だけがこの面を担える。両半分を一括で落としていたのが #1798 であり、イベントは届いているのに扱いを述べる面が無い状態になっていた。

hook が運ぶのは terse な re-arm であって正本の写しではない。関連性判定と destructive consume の正本は [5. Notifications](5.-Notifications)、前景取り込み手続きの正本は `rules/operations/main-agent-procedures.md` の `## Foreground webhook notification intake` に従う。

### post-tool-use.sh

Expand Down Expand Up @@ -191,7 +191,7 @@ node 不在時の挙動:node は jq より確度の高い前提だが保証で
│ └── <skill-name>/SKILL.md # skill auto-invocation(flat 命名、例:operations-on-commit, model-agentic-search, evolution-loop, model-pair-review 等)
├── hooks/
│ ├── on-session-start.sh # Cold-start Synthesis 素材収集(diff-only 出力)
│ ├── on-user-prompt.sh # 通知取り込みリマインダーのみ(Character は output-styles で常駐)
│ ├── on-user-prompt.sh # Gate re-arm + 通知取り込み re-arm(Character は output-styles で常駐)
│ └── post-tool-use.sh # PR 作成時のサブ issue 参照自動補完のみ
└── state/
├── .gitignore # `*\n!.gitignore`(hook runtime state をバージョン管理から除外)
Expand Down
7 changes: 4 additions & 3 deletions docs/B.-Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,14 @@ webhook 通知がセッションへ届く方法を指定します。`mcp__github

| 値 | 動作 |
|----|------|
| 未設定 / `poll` | 毎ターン開始時に on-user-prompt hook がポーリングリマインダーを出力する(既定、後方互換) |
| `channel` | MCP channel がリアルタイムにイベントを配信するため、hook のポーリングリマインダーをスキップする |
| `mcp_hook` | UserPromptSubmit の `type: "mcp_tool"` hook が `mcp__github-webhook-mcp__get_pending_status` を直接呼び出し、結果を prompt context に注入する。bash hook のポーリングリマインダーはスキップされる(`github-webhook-mcp >= v0.11.3` が前提) |
| 未設定 / `poll` | 毎ターン開始時に on-user-prompt hook が呼び出し指示を出力し、AI 自身に MCP ツールを呼ばせる(既定、後方互換) |
| `channel` | MCP channel がリアルタイムにイベントを配信するため、hook の呼び出し指示をスキップする |
| `mcp_hook` | UserPromptSubmit の `type: "mcp_tool"` hook が `mcp__github-webhook-mcp__get_pending_status` を直接呼び出し、結果を prompt context に注入する。hook の呼び出し指示はスキップされる(`github-webhook-mcp >= v0.11.3` が前提) |

注意:

- 値を切り替えても webhook 通知の前景判定ルールは変わりません。transport(呼び出し主体)が変わるだけです
- したがってスキップされるのは**呼び出し指示だけ**です。取り扱い指示(前景報告フィルタと `mark_processed` の re-arm)は全モードで毎ターン出力されます。これを代替するものはどのモードにも無く、発火時刻がターン境界である以上、hook 以外に担える面が無いためです(#1798)
- この設定は on-user-prompt hook が実行時に Li+config.md から読み取ります。bootstrap での追加アクションは不要です
- `mcp_tool` の hook entry は `adapter/claude/hooks-settings.md` の default テンプレートに含まれており、bootstrap によって `.claude/settings.json` に自動配置されます。**手動追加は不要**になりました(旧仕様では opt-in に手動編集が必要でした)
- 配信が実際に AI 文脈へ届く前提条件:
Expand Down
Loading
Loading