Persistent Monitor: silent reader stall while task shows running; FIFO notification drain with no catch-up or priority; unmarked Monitor stops; exit 144 orphans pipeline
Summary
We drive long-running orchestration off the persistent Monitor tool (Monitor(command="tail -n +1 -f <events file>", persistent=true)): sub-agent managers append turn-end/ping lines to an ndjson events file, and the Monitor's task-notifications are the parent session's only push channel. Across one long session (2026-08-01/02, macOS, Claude Code CLI) we hit four distinct Monitor/notification defects. We have full transcripts, captured task-output files, and process forensics for all four; excerpts below.
All timestamps UTC. "Injection" = a queue-operation/notification entry appearing in the session transcript.
Defect 1: Monitor reader silently stops consuming a running task's stdout
The most serious one. A Monitor (id bwx114vo2, armed 12:39:26Z) delivered notifications normally until 21:05:11Z, then never enqueued another one, while:
- The underlying pipeline (
tail -f | grep --line-buffered | grep | grep) remained alive:lsof -p <tail_pid>showed the correct file open with matching inode and current size;ps -g <pgid>showed every stage in state SN. New matching lines were verifiably appended to the tailed file after 21:05 (21:00:57Z and 21:04:59Z lines present both in the source file and in the Monitor's own captured task-output file). TaskOutputon the Monitor task reportednot_ready/running the entire time (checked at 22:05:21Z, an hour into the stall).TaskStopon it succeeded normally.- A freshly armed Monitor on the same file immediately read and delivered the same lines the stalled one never surfaced.
So the break is strictly between the task's stdout and the harness's capture/notification loop: the pipe produced the lines (they are in the captured output file), the task was "running", but nothing was enqueued for over an hour, with no failure signal anywhere. An urgent operator-facing line sat undelivered the whole time and was only found by manually diffing the raw file against the transcript.
Defect 2: notification drain is one-per-new-output-event, FIFO, with no catch-up and no priority
Each notification injection coincides exactly with a new output line arriving, but the injected content is the queue head, so a backlog never shrinks while the stream is quiet and grows without bound while it is busy:
- Monitor
bkav81d9wran a constant ~5-event backlog for hours; e.g. the injection at 12:12:24Z delivered an event stamped 10:42:20Z (90 minutes stale). - An urgent line appended at 06:47:16Z was injected at 08:47:28Z, two hours late, because it was queued behind two informational keepalive lines and an error line, each consuming one drain slot.
There appears to be no mechanism to drain more than one queued notification per trigger, no catch-up when the queue is nonempty and the session is otherwise idle, and no way to mark a line urgent.
Defect 3: notifications cannot reach a turn blocked on AskUserQuestion, then flush all at once
While a turn was blocked on AskUserQuestion from 08:47Z to 14:52Z, ~95 notifications enqueued and none could be injected; on the human's answer at 14:52:20Z they flushed as one giant batch of attachments. Arguably by design, but combined with defect 2 it means an urgent notification has no path past a human-blocked turn, and the eventual flush buries it in noise.
Defect 4: exit 144, and Monitor stops that "leave no transcript marker"
- The same session earlier had a Monitor die with
<status>failed</status> ... script failed (exit 144)27 minutes after its last output. The kill did not reap the shell pipeline: the tail/grep tree was found reparented to init and still running 11 hours later. What exit 144 means is undocumented as far as we can tell. - A later stopped-notice's own text states that Monitors stopped "via the UI, Monitor timeout, or agent teardown ... leave no transcript marker". Combined with defect 1, this means a session cannot distinguish "Monitor healthy, nothing happened" from "Monitor gone/stalled, events lost" from inside the conversation, which breaks any workflow that relies on silence meaning nothing happened.
Environment
- Claude Code CLI on macOS (Darwin 25.5.0), persistent Monitor over
tail -n +1 -fof an append-only ndjson file on local disk. - Reproduced across three separately armed Monitors in one session; defect 1 reproduced on a Monitor armed fresh after a prior one was stopped.
What would help
- A liveness/watermark signal for Monitor: e.g. TaskOutput exposing "last byte consumed at T / N lines queued", so a stall is detectable.
- Draining the whole queue (or up to a budget) per trigger, and/or a catch-up tick while the queue is nonempty.
- Any priority or at-least-once guarantee usable for operator-critical lines.
- Documented semantics for exit 144, and reaping the spawned process tree when a Monitor dies.
- A transcript marker for every Monitor stop, whatever the cause.