From 4e1b8a9cf21b0ab8af58f8b879075700cae28e65 Mon Sep 17 00:00:00 2001 From: jedarden Date: Mon, 25 May 2026 15:21:23 -0400 Subject: [PATCH] =?UTF-8?q?docs:=20gap-review=20round=202=20=E2=80=94=20fi?= =?UTF-8?q?x=20hook-wiring=20self-contradiction?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - HIGH: settings.json example wired PermissionRequest to a bare curl that drops the env-only $TMUX_PANE; route all hooks through trailboss-emit.sh (which injects $TMUX_PANE) and note SessionStart is not special - MEDIUM: remove the stray trailboss-register.sh (contradicted "registry self-heals on every emit"); single emitter script - LOW: add struck-through SubagentStop row to plan detection table; clarify disler's store is append-only (not "read-only") Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/plan/plan.md | 1 + docs/research/claude-code-mechanics.md | 18 +++++++++++------- docs/research/related-work.md | 4 ++-- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/docs/plan/plan.md b/docs/plan/plan.md index e238411..042c234 100644 --- a/docs/plan/plan.md +++ b/docs/plan/plan.md @@ -125,6 +125,7 @@ stuck conditions: a session waiting at a permission prompt is mid-turn and does | `SessionStart` | Register the session; re-assert `session_id → pane`. | Confirmed firing (probe) | | `SessionEnd` | Retire the session. | Exists; firing not yet probed | | ~~`Notification`~~ | Dropped — `Stop` + `PermissionRequest` cover every stuck case; the dead-letter queue just fills and drains. | Not used | +| ~~`SubagentStop`~~ | Ignored — a subagent finishing is not a human-input point. | Not used | Both `Stop` and `PermissionRequest` enqueue a plain stuck item with no priority difference. diff --git a/docs/research/claude-code-mechanics.md b/docs/research/claude-code-mechanics.md index fbf4d2a..9bbf6e3 100644 --- a/docs/research/claude-code-mechanics.md +++ b/docs/research/claude-code-mechanics.md @@ -9,24 +9,28 @@ Code's hook surface evolves and some semantics are undocumented. ## 1. DETECT — which hook says a session is blocked Hooks live in `~/.claude/settings.json` (user-global) or `.claude/settings.json` (per-project). -Each `command` hook receives the **event JSON on stdin**, so a one-liner that pipes stdin to -the collector is enough: +Each `command` hook receives the **event JSON on stdin**. **Every** hook routes through the same +`trailboss-emit.sh` — a thin wrapper that forwards stdin *and* injects `$TMUX_PANE` from the +environment (the pane is not in the payload; see §2). A bare `curl` of stdin alone is **not** +enough, because it would drop the env-only pane mapping. `SessionStart` is **not** special — it +uses the same emitter; the registry self-heals on every event: ```json { "hooks": { - "PermissionRequest": [ - { "hooks": [ { "type": "command", - "command": "curl -s -X POST http://localhost:4000/event --data-binary @-" } ] } - ], + "PermissionRequest": [ { "hooks": [ { "type": "command", "command": "~/.claude/trailboss-emit.sh" } ] } ], "Stop": [ { "hooks": [ { "type": "command", "command": "~/.claude/trailboss-emit.sh" } ] } ], "UserPromptSubmit": [ { "hooks": [ { "type": "command", "command": "~/.claude/trailboss-emit.sh" } ] } ], - "SessionStart": [ { "hooks": [ { "type": "command", "command": "~/.claude/trailboss-register.sh" } ] } ], + "SessionStart": [ { "hooks": [ { "type": "command", "command": "~/.claude/trailboss-emit.sh" } ] } ], "SessionEnd": [ { "hooks": [ { "type": "command", "command": "~/.claude/trailboss-emit.sh" } ] } ] } } ``` +`trailboss-emit.sh` is essentially: +`curl -s -X POST http://localhost:4000/event --data-binary @- -H "X-Tmux-Pane: $TMUX_PANE"` +(or it merges `$TMUX_PANE` into the JSON body before POSTing). + Events relevant to "needs a human": | Event | Meaning for the queue | Confidence | diff --git a/docs/research/related-work.md b/docs/research/related-work.md index cc9a4a8..1ab3871 100644 --- a/docs/research/related-work.md +++ b/docs/research/related-work.md @@ -15,8 +15,8 @@ layer** Trail Boss needs: hooks → collector → live UI, keyed by `session_id` **How Trail Boss differs:** it is *observability*, not *action*. It shows *that* a session is waiting; Trail Boss adds the missing half — surfacing the blocked session and **navigating the operator to the live pane** to act on it (it never injects input; see "Navigator, not relay" in -the plan). Its collector is a strong starting point to fork; Trail Boss extends the read-only -event store with a self-healing session→pane registry and a tmux navigation layer. It reuses the +the plan). Its collector is a strong starting point to fork; Trail Boss extends disler's +append-only event store with a self-healing session→pane registry and a tmux navigation layer. It reuses the SQLite event store, not the WebSocket/browser-UI layer (presentation is tmux, not a web app). ### [`langchain-ai/agent-inbox`](https://github.com/langchain-ai/agent-inbox)