Skip to content

Test and harden Notification hook reliability for "waiting" status #1

@amahpour

Description

@amahpour

Problem

The "waiting" (yellow) status in the dashboard is the most important signal in the cockpit — it tells the operator that a Claude Code session is blocked waiting for permission approval. This status is driven entirely by the Notification hook event type firing from Claude Code.

Current implementation (server/hooks.py:151-156):

elif event_type == "Notification":
    base_updates["status"] = "waiting"
    message = event_data.get("message", "")
    if message:
        base_updates["task_description"] = message
    session = await db.update_session(session_id, **base_updates)

The hook is received via POST /api/hooks (server/routes/api.py:73-80) and the status update is broadcast to all dashboard clients via WebSocket (server/routes/ws.py:19-33).

The concern: The Notification event type has been inconsistent across Claude Code versions. If the hook doesn't fire, the session appears "working" when it's actually blocked — the operator has no idea a session needs attention. This is a silent failure with high impact.

What needs to happen

1. Comprehensive test coverage for the Notification hook pipeline

Write end-to-end tests that verify the full path:

  • Hook ingestion: Simulate a Notification hook payload hitting POST /api/hooks and verify the session status transitions to "waiting" in the database.
  • WebSocket broadcast: Verify that connected dashboard clients receive a session_update message with status: "waiting" and the correct task_description when a Notification event arrives.
  • Status transitions: Test the full lifecycle: idleworking (PreToolUse) → waiting (Notification) → working (PostToolUse) → idle (Stop). Ensure no status is skipped or stuck.
  • Edge cases:
    • Notification event with empty message field
    • Notification event with missing session_id
    • Notification event for a session that doesn't exist yet (should auto-create per current logic at hooks.py:100-103)
    • Rapid successive Notification events for the same session
    • Notification followed immediately by a Stop event (race condition)

2. JSONL-based fallback detection

If the Notification hook proves unreliable across Claude Code versions, implement a fallback that detects permission-waiting state from the JSONL conversation file.

Approach:

  • In server/watcher.py, when processing new JSONL lines, look for patterns that indicate Claude is waiting for permission (e.g., tool use entries that haven't been followed by a tool_result within a timeout window).
  • If detected and the session status is not already "waiting", update the status as a fallback.
  • This adds latency (up to the 1-second debounce in watcher.py:19 plus file system notification delay) compared to the hook approach, but provides a safety net.

3. Staleness detection for "waiting" status

Currently server/stale.py checks for stale sessions, but there should be specific handling for sessions stuck in "waiting" status:

  • If a session has been in "waiting" for an unusually long time (e.g., >10 minutes), surface this prominently in the dashboard — the operator may have missed it.
  • Consider adding a notification sound or browser notification for "waiting" transitions.

Acceptance criteria

  • Unit tests covering the Notification hook → DB update → WebSocket broadcast pipeline
  • Integration test simulating the full hook payload format that Claude Code sends
  • Test for status transition correctness across the full session lifecycle
  • Decision documented on whether a JSONL fallback is needed (based on testing across Claude Code versions)
  • If fallback is needed, implementation with tests

Context

The hook event flow is: Claude Code fires hook → POST /api/hooksprocess_hook_event() in hooks.py → DB update → broadcast_session_update() → WebSocket to all dashboard clients. The _notify_update callback is wired in main.py:23 via set_update_callback(broadcast_session_update).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingtesting

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions