Hook lifecycle gap: no observability between PreToolUse and PostToolUse (stuck tool detection)
Problem
There is a complete observability blackout between the PreToolUse hook (tool approved) and the PostToolUse hook (tool completed). During this gap:
- No event fires to indicate a tool has started executing
- No heartbeat or progress signal exists during execution
- No way to distinguish between "tool is running normally" vs "tool is stuck/hanging"
- This is especially severe inside subagents, where the parent collapses activity into
+N more tool uses
Real-world scenario
Running a multi-feature lifecycle (/lifecycle V09) that spawns subagents to execute plans. A subagent calls Edit on a file. The edit appears to hang — 15+ minutes with no output, no status change, no indication of progress. The user has no way to know if:
- Claude is still thinking (extended thinking before the tool call)
- The tool is executing but slow
- The tool or API call is genuinely stuck
- The subagent has crashed silently
The only recourse is to use BTW (interrupt) to ask "are you still working?" — which disrupts the execution flow.
What exists today
| Hook | When it fires | What it tells you |
|------|--------------|-------------------|
| PreToolUse | Before user approval | Tool is about to be called |
| (nothing) | During execution | Complete blackout |
| PostToolUse | After completion | Tool finished, here's the result |
What's needed
A ToolExecutionStart event or DuringToolUse heartbeat that fires when a tool begins executing (after approval, before completion). This would enable:
- Stuck detection — if
ToolExecutionStartfired 5 minutes ago with noPostToolUse, something is wrong - Progress tracking — external monitoring tools can show "Edit running on ClueService.cs for 3m"
- Subagent visibility — parent process can surface what tools subagents are currently running
- Timeout implementation — users could set per-tool timeouts based on execution start time
Proposed solutions (any would help)
Option A: ToolExecutionStart hook — fires immediately after approval, before execution begins. Payload includes tool name, parameters, and a correlation ID matching the eventual PostToolUse.
Option B: Heartbeat during execution — periodic event (every 30s) during long-running tool calls. Payload: tool name, elapsed time, correlation ID.
Option C: Execution duration in status line — the status line (or a new --verbose stream event) shows the currently-executing tool and a running timer.
Option D: Sub-agent event propagation — at minimum, propagate sub-agent tool events to the parent stream so monitoring tools can observe them (related to #43553).
Related issues
This is the root cause behind several open issues that describe symptoms of the same gap:
- #43553 — Sub-agent internal events invisible in
stream-json - #42273 — Zero visibility into background agent execution
- #43437 — No progress indicator during long-running operations
- #43342 — MCP tools hang indefinitely with no timeout or feedback
- #40453 — Generic status messages provide zero signal about actual progress (user quote: "The #1 reason I use BTW is not to add context — it's just to ask 'are you still working?'")
- #43071 — Cannot distinguish extended thinking from API hangs
Environment
- Claude Code CLI + Desktop (Windows 11)
- Opus 4.6 (1M context)
- Heavy use of subagents via Agent tool for parallel execution
- Custom PostToolUse hook logging JSONL activity (works great — but only fires after completion)
- 2-4 parallel Claude Code sessions running simultaneously
Who this affects
Anyone using Claude Code for multi-step autonomous workflows where subagents execute plans, especially:
- TTKWorkflow-style skill orchestration (
/lifecycle,/execute-plans) - CI/CD pipelines using
--output-format stream-json - Teams running background agents with
run_in_background: true - Any workflow where Claude runs for 10+ minutes unattended
This issue has 5 comments on GitHub. Read the full discussion on GitHub ↗