2026-07-24T07:13:59.851Z

Claude Code Monitoring: Catch Permission Waits and Missing Outcomes

A practical three-layer design for combining Claude Code telemetry, lifecycle hooks, and deterministic checks so activity is not mistaken for a healthy outcome.

Claude Code monitoring needs three layers, not one dashboard. Use Claude Code’s official OpenTelemetry feed for consumption and activity, lifecycle hooks for waits and terminal failures, and a project owned check for the result you actually requested. If you omit the third layer, a session can have clean traces, successful tool calls, and a polished final response while the expected patch, test result, or file is still missing. The reasonable default is deliberately small: export redacted metrics and events, log six lifecycle events without their free text payloads, then evaluate the last event against a task specific completion predicate. Do not collect prompts or raw tool content merely to decide whether a run needs attention. This guide builds that design from the current Claude Code schemas and tests the decision order against seven sessions. It does not assume that a tool call is progress, that a pause is failure, or that Stop means the work is complete. Start with three different questions Monitoring becomes clearer when each signal answers one question and is forbidden from answering the other two. Layer Question it can answer Signals What it cannot prove Telemetry What did Claude Code consume or execute? sessions, API requests, tokens, estimated cost, tool results, tool decisions, duration whether the requested outcome is correct Lifecycle Why is this session quiet or ending? permission request, notification, background task, scheduled wakeup, stop, API ended turn, session end whether a file, test, or external result is valid Outcome Did this task produce the promised result? file hash, test exit status, schema validation, API response, signed artifact why the session waited or how much it cost The official Claude Code monitoring documentation exposes metrics through the OTel metrics protocol, events through logs/events, and optional distributed traces. Documented metrics include session count, lines changed, commits, pull requests, active time, tokens, and estimated cost. Events add prompt correlation, API results, tool results, and permission decisions. That is excellent evidence for the first layer. It is not a completion contract. The distinction matters in ordinary work. A successful Write tool event says a write completed. It does not say the intended file was written to the right location, that the resulting program compiles, or that the user asked for that file. Token and cost curves can reveal runaway consumption, but a low cost session can still stop one step before the deliverable. Add lifecycle evidence with Claude Code hooks Claude Code’s hooks reference provides a second layer that a usage only dashboard misses. Four events are particularly useful: PermissionRequest fires when a permission dialog is about to be shown. If it is the newest unresolved event, the session is waiting for a person; it is not stalled. Stop fires when the main agent finishes responding. Current input can include background tasks and session crons , so a stopped turn may still be waiting for a shell task, subagent, Monitor task, workflow, MCP task, or scheduled wakeup. StopFailure fires instead of Stop when an API error ends the turn. Its documented error classes include rate limit, overload, authentication, billing, invalid request, missing model, server error, and maximum output tokens. SessionEnd records why the session terminated. It is useful for cleanup and audit, but it cannot block termination. PostToolUse , PostToolUseFailure , Notification , and PreCompact add useful context. Keep their semantics narrow: recent PostToolUse is evidence of activity; repeated PostToolUseFailure is evidence of tool trouble; PreCompact marks a context transition worth correlating with later behavior. None is a universal health verdict. For a privacy minimal collector, retain only a timestamp, a locally hashed session identifier, event name, tool name, error class, notification type, and counts of background tasks or scheduled wakeups. Omit transcript path , cwd , last assistant message , Bash commands, tool input, and notification text unless a diagnosed use case justifies them. The official OTel defaults support the same restraint. Prompt text, assistant response text, tool arguments, tool input/output content, and raw API bodies are disabled by default. Enabling OTEL LOG RAW API BODIES can expose the full conversation history; it should never be a casual troubleshooting switch. Turn the latest evidence into a state The decision order below is small enough to inspect. It classifies the last event for each session, while an external verifier supplies outcome verified when a turn stops. The order is intentional. A terminal API failure outranks a recent activity event. An unresolved approval is waiting, not a timeout. Background work prevents a Stop event from being treated as completion. Only after those cases are excluded does the verifier decide between complete and outcome missing . The retained test fixture uses seven sessions and a 15 minute example threshold: Running the fixture at a fixed timestamp reproduces all seven lines: This is a decision rule, not a production daemon. The 15 minute threshold is wrong for a two minute lint job and wrong for a two hour build. Set freshness from the expected cadence and duration of the work, then keep an uncertain path for missing or contradictory evidence. Define completion outside the conversation The classifier’s only application specific input is outcome verified . That bit should come from a deterministic check whenever possible, not from searching the final assistant message for “done.” For a code change task, useful completion might require all of these: 1. the expected files differ from the starting commit; 2. the focused test command exits successfully; 3. the generated artifact decodes or the package can be imported; 4. the result remains inside the approved repository and scope. For a documentation task, require the destination file, frontmatter or schema validation, all cited local paths, and any link checker that the repository already trusts. For a data export, check the expected file, parse it, validate required columns, and compare row counts with the source boundary. For an API change, run the contract test rather than accepting an HTTP request that merely returned. The monitor should store the verifier’s name, exit status, observation time, and a digest of the result—not a fabricated explanation. When no deterministic predicate exists, record outcome unknown . An unknown outcome can request review; it should not silently become healthy. Alert on the next safe action Seven states do not need seven alarm sounds. Route each state to the smallest useful action: State Default action working do nothing waiting human notify the responsible person with the permission category, without approving it waiting background show the dependency and its freshness; do not restart the session failed: expose the error class and bounded retry policy outcome missing show the failed completion predicate and preserve the work for inspection stalled recheck reachability and expected duration before proposing one bounded nudge complete retain evidence and close the issue This routing prevents two expensive mistakes. First, it avoids retrying an agent that is correctly waiting for authority. Second, it avoids celebrating a conversational stop when the project evidence says the outcome is absent. Automated recovery needs tighter boundaries than monitoring. A rate limit failure may be retryable after a backoff; an authentication failure usually needs a person; a permission prompt must not be auto approved merely because it is old. After any intervention, re run the completion predicate. A successful command is evidence of activity, not proof that the original task recovered. Apply the design without over collecting A practical rollout can stay incremental: 1. Enable Claude Code telemetry with metrics and events, leaving all content logging switches off. 2. Confirm claude code.session.count or claude code.user prompt reaches the collector before building alerts. 3. Add local hooks for PermissionRequest , Stop , StopFailure , Notification , PreCompact , and SessionEnd . 4. Normalize those hook payloads into the minimal envelope; hash identifiers locally and drop paths and free text. 5. Define one deterministic completion predicate for one consequential task. 6. Replay synthetic events for every state before notifying anyone. 7. Add an alert only when its owner and safe next action are explicit. Version the normalizer. Claude Code documents minimum versions for several fields, and transcript internals are explicitly not a stable contract. Prefer the hook and OTel fields that the current documentation exposes; do not build a long lived monitor by scraping terminal pixels or assuming a private transcript shape will never change. The useful boundary for Sidewisp Claude Code already provides strong raw signals. The operational gap is turning those signals into a restrained health decision: working, waiting, failed, stale, or missing its promised outcome—then showing the evidence and safest next move. Sidewisp is currently in private preview. Its public site and article system are live, but production Claude Code monitoring adapters, agent health collection, and recovery are not generally shipped. The intended role is a health layer alongside existing runtimes, not a replacement runtime, mandatory model gateway, or autonomous fixer. If that boundary matches how you operate coding agents, the private preview waitlist is the appropriate next step. Until then, the three layer pattern in this guide is usable on its own: telemetry for activity, hooks for lifecycle, and deterministic checks for outcomes.