TaskBeacon vs. the official notification hook

Last measured 2026-08-03 · macOS 26.6, Apple Silicon · c9watch v0.8.1 (223b61c) · claude-status-bar 19281b2 · claude-status 6a4ef50

Search for "how do I get notified when Claude Code finishes" and almost every answer points at the same thing: add a Notification hook to ~/.claude/settings.json. That is a reasonable floor, and a lot of menu bar tools are built directly on top of it.

This page is about where that floor has holes — each one with an issue number you can open yourself — what a hook fundamentally cannot see no matter how well it is written, and what happened when we actually measured the alternatives. Where TaskBeacon is the weaker option is in section 5, not buried in a footnote.

1. Four documented failure modes of the Notification hook

All four are filed in anthropics/claude-code and were re-checked on 2026-08-03. Three of the four are closed — this is not a claim that the hook is broken today. It is a claim that these four classes of failure are on record, and that a monitor which has only this one signal has no answer when any of them happens.

Failure mode Issue Status
Alert fatigue. idle_prompt fires after every response, so "your turn" and "waiting on your approval" are indistinguishable #12048 Closed as duplicate
Silent inside the VS Code extension. The same settings.json fires normally in the CLI and produces no events in the native extension #59718 Closed as duplicate
Permission events lost during thinking. The prompt is visible in the TUI while the hook never fires #58909 Closed (version regression, since fixed)
Multi-second delay between the event and the hook actually being invoked #19627 Open

What TaskBeacon does instead

2. The blind spots no hook can see

These are not bugs in anyone's implementation. They are places where Claude Code emits no event at all, so a purely hook-driven monitor is structurally blind and a transcript-driven one is guessing from file timestamps.

The approval blind spot

You click approve on a permission prompt. No hook fires at that moment. The next event, PostToolUse, arrives only when the command finishes. So between your click and the end of a long command, a naive hook-driven monitor keeps showing "awaiting permission".

We reproduced this against claude-status-bar's own shipped hook code, replaying a real event sequence in an isolated environment:

① PreToolUse state=tool "Running command" ② PermissionRequest state=permission "Awaiting permission" ★ you click approve — Claude Code emits nothing here ★ ③ +2s (running) state=permission "Awaiting permission" ← already running ④ +5s (running) state=permission "Awaiting permission" ⑤ PostToolUse state=thinking "Thinking…" ← the only release

Its permission state is released by PostToolUse, a 7200-second cap, or an "interrupted by user" line in the transcript (Sources/main.swift:1207-1216). In practice: a foreground command shows "awaiting permission" for as long as it runs.

TaskBeacon closes this by probing the terminal's process tree — if real work is running under that tty, the session is working, not blocked — with a timestamp guard so that a new prompt raised while a leftover shell is still alive is not swallowed by the same probe.

A background subagent asking for permission

When a background subagent hits a permission prompt, the request does not reach the main terminal's hooks — the main turn has already emitted Stop. TaskBeacon keeps a per-terminal ledger of background agents and fills this case in from the daemon's session state, so the row goes red even though nothing was delivered to that tty.

Background shells still running

When a run_in_background command is still going after the main turn ends, the session is available to you — so it is correctly "done", not "working". The useful thing is not a different colour, it is saying so: TaskBeacon shows the count of background shells still running on that row.

3. What we measured

Two evidence types were required for anything quoted here: a citable line of source and a reproduction on a real machine. Single-evidence findings exist in our working notes but are deliberately not quoted on this site.

Sample. One machine, a five-minute window of real work sampled every 5 seconds: 58 sample points, 348 observations. The reference was the status field Claude Code itself maintains in ~/.claude/sessions/<pid>.json.

Reference state Observations c9watch correct TaskBeacon correct
waiting — genuinely blocked on you18 0 / 1818 / 18
busy — the model is working161 124 / 161161 / 161

The cliff lands exactly on its own threshold

Breaking c9watch's accuracy on busy sessions down by how old the transcript file was at that moment:

Transcript age Correct Reported instead
0–10 s52 / 57 — 91%Connecting ×5
10–20 s40 / 41 — 98%Connecting ×1
20–30 s30 / 32 — 94%Connecting ×2
> 30 s0 / 27 — 0% WaitingForInput ×26, Connecting ×1

The drop lands precisely on the 30-second freshness threshold in its own source (src-tauri/src/session/status.rs:80). A monitor that infers liveness from file mtime goes blind exactly when the transcript goes quiet — and a session sitting on a permission prompt is a session whose transcript is not moving. In our runs, sessions parked on a prompt had transcripts untouched for 591 and 668 seconds.

Two more results from the same run:

Method, and why it did not touch anything

Nothing was installed into the shared configuration to produce these numbers. Two zero-pollution paths were used instead: claude-status-bar's own shipped hook code was replayed under an isolated HOME with payloads captured from a real machine, and c9watch was exercised through its official signed pre-built CLI in read-only mode. The global ~/.claude/settings.json was never modified.

4. Where TaskBeacon is the weaker choice

Every claim above has a boundary. These are ours.

Claims we removed after measuring, because they were wrong. We previously wrote that tools keyed on session IDs collapse sibling sessions in one VS Code window into a single row — they do not; all four tools we tested keep them separate. We also wrote that claude-status-bar mishandles thinking-time notifications — it does not; it handles that case correctly. Those lines are gone. This section stays because a comparison page that only ever discovers things in its own favour is not worth reading.

5. Sources

Numbers on this page are re-measured each quarter, and whenever either tool ships a release that touches its status logic. A version bump on any side can invalidate a row — the versions each figure was taken against are printed at the top of this page for exactly that reason.

Download TaskBeacon