# Prompt changes in v2.1.310

> 7 changes since v2.1.309, landing in 18 of 23 prompts.

[Web version](https://changelogs.core-directive.com/prompts/2.1.310)

## 1. "Monitor" added as a line in a system reminder

System reminder text · line 7

The single word "Monitor" now appears on line 7 of an injected system reminder. The diff shows no surrounding context, so it may be a new entry in a list such as a tool name, but that cannot be confirmed. It landed in only 1 of 23 captures, so it is likely specific to one setup.

- +1 / -0 words
- Landed in 1 of 23 prompts (system reminders 1/8)

```diff
+Monitor
```

## 2. New Monitor tool streams background script output as chat notifications

Monitor tool description · whole description added

Adds a Monitor tool that runs a script and turns each stdout line into a notification while Claude keeps working. Claude should use Bash run_in_background for a single notification and save Monitor for repeated events. The description also covers line-buffered pipes, filters that catch failure states as well as success, keeping output volume low, a 5-minute default timeout (10 at most) with re-arming, and a WebSocket source. It appeared in only 2 of 6 captures, so it may be limited to certain setups.

- New in this release
- +1035 / -0 words
- Landed in 3 of 23 prompts (main prompt 2/6, system reminders 1/8)

```diff
+Start a background monitor that streams events from a long-running script. Each stdout line is an event — you keep working and notifications arrive in the chat. Events arrive on their own schedule and are not replies from the user, even if one lands while you're waiting for the user to answer a question.
+Pick by how many notifications you need:
+- **One** ("tell me when the server is ready / the build finishes") → use **Bash with `run_in_background`** and a command that exits when the condition is true, e.g. `until grep -q "Ready in" dev.log; do sleep 0.5; done`. You get a single completion notification when it exits.
+- **One per occurrence, until the monitor expires (re-arm to continue)** ("tell me every time an ERROR line appears") → Monitor with an unbounded command (`tail -f`, `inotifywait -m`, `while true`).
+- **One per occurrence, until a known end** ("emit each CI step result, stop when the run completes") → Monitor with a command that emits lines and then exits.
+Your script's stdout is the event stream. Each line becomes a notification. Exit ends the watch.
+  # Each matching log line is an event
+  tail -f /var/log/app.log | grep --line-buffered "ERROR"
+  # Each file change is an event
+  inotifywait -m --format '%e %f' /watched/dir
+  # Poll GitHub for new PR comments and emit one line per new comment
+  last=$(date -u +%Y-%m-%dT%H:%M:%SZ)
+  while true; do
+    now=$(date -u +%Y-%m-%dT%H:%M:%SZ)
+    gh api "repos/owner/repo/issues/123/comments?since=$last" --jq '.[] | "\(.user.login): \(.body)"'
+    last=$now; sleep 30
+  done
+  # Node script that emits events as they arrive (e.g. WebSocket listener)
+  node watch-for-events.js
+  # Per-occurrence with a natural end: emit each CI check as it lands, exit when the run completes
+  prev=""
+  while true; do
+    s=$(gh pr checks 123 --json name,bucket)
+    cur=$(jq -r '.[] | select(.bucket!="pending") | "\(.name): \(.bucket)"' <<<"$s" | sort)
+    comm -13 <(echo "$prev") <(echo "$cur")
+    prev=$cur
+    jq -e 'all(.bucket!="pending")' <<<"$s" >/dev/null && break
+    sleep 30
+  done
+**Don't use an unbounded command for a single notification.** `tail -f`, `inotifywait -m`, and `while true` never exit on their own, so the monitor stays armed until timeout even after the event has fired. For "tell me when X is ready," use Bash `run_in_background` with an `until` loop instead (one notification, ends in seconds). Note that `tail -f log | grep -m 1 ...` does *not* fix this: if the log goes quiet after the match, `tail` never receives SIGPIPE and the pipeline hangs anyway.
+**Script quality:**
+- Every pipe stage must flush per line or matches sit in its buffer unseen: `grep` needs `--line-buffered`, `awk` needs `fflush()`. `head` cannot flush at all — `| head -N` delivers nothing until N matches accumulate, then ends the stream.
+- In poll loops, handle transient failures (`curl ... || true`) — one failed request shouldn't kill the monitor.
+- Poll intervals: 30s+ for remote APIs (rate limits), 0.5-1s for local checks.
+- Write a specific `description` — it appears in every notification ("errors in deploy.log" not "watching logs").
+- Only stdout is the event stream. Stderr goes to the output file (readable via Read) but does not trigger notifications — for a command you run directly (e.g. `python train.py 2>&1 | grep --line-buffered ...`), merge stderr with `2>&1` so its failures reach your filter. (No effect on `tail -f` of an existing log — that file only contains what its writer redirected.)
+**Coverage — silence is not success.** When watching a job or process for an outcome, your filter must match every terminal state, not just the happy path. A monitor that greps only for the success marker stays silent through a crashloop, a hung process, or an unexpected exit — and silence looks identical to "still running." Before arming, ask: *if this process crashed right now, would my filter emit anything?* If not, widen it.
+  # Wrong — silent on crash, hang, or any non-success exit
+  tail -f run.log | grep --line-buffered "elapsed_steps="
+  # Right — one alternation covering progress + the failure signatures you'd act on
+  tail -f run.log | grep -E --line-buffered "elapsed_steps=|Traceback|Error|FAILED|assert|Killed|OOM"
+For poll loops checking job state, emit on every terminal status (`succeeded|failed|cancelled|timeout`), not just success. If you cannot confidently enumerate the failure signatures, broaden the grep alternation rather than narrow it — some extra noise is better than missing a crashloop.
+**Output volume**: Every stdout line is a conversation message, so the filter should be selective — but selective means "the lines you'd act on," not "only good news." Never pipe raw logs; filter to exactly the success and failure signals you care about. Monitors that produce too many events are automatically stopped; restart with a tighter filter if this happens.
+Stdout lines within 200ms are batched into a single notification, so multiline output from a single event groups naturally.
+The script runs in the same shell environment as Bash. Exit ends the watch (exit code is reported). Every monitor expires after `timeout_ms` (default 5 minutes, at most 10 minutes): it is killed and you get one notice with the event count. Re-arm it if you still need the watch; for a long watch (PR monitoring, log tails) set `timeout_ms` to the maximum and re-arm on each expiry, and widen the filter if an expiry with no events was unexpected. Use TaskStop to cancel early.
+**ws source** — open a WebSocket and stream each incoming text frame as an event. No shell, no polling: the server pushes, you get notified.
+  Monitor({
+    ws: {url: 'wss://events.example.com/stream', protocols: ['v1']},
+    description: 'deploy events',
+  })
+Each text frame becomes one notification (multiline frames stay as one event). Binary frames are reported as `[binary frame, N bytes]` rather than passed through. Socket close ends the watch with the close code surfaced; errors are surfaced before close. Same rate limiting as bash — a firehose will be suppressed and eventually stopped, so subscribe to a filtered feed where one exists.
+Prefer this over `command: 'websocat wss://…'` — it avoids the extra process and line-buffering pitfalls. Use bash when you need to transform or filter frames with shell tools before they become events.

+Start a background monitor that streams events from a long-running script. Each stdout line is an event — you keep working and notifications arrive in the chat. Events arrive on their own schedule and are not replies from the user, even if one lands while you're waiting for the user to answer a question.
+Pick by how many notifications you need:
+- **One** ("tell me when the server is ready / the build finishes") → use **Bash with `run_in_background`** and a command that exits when the condition is true, e.g. `until grep -q "Ready in" dev.log; do sleep 0.5; done`. You get a single completion notification when it exits.
+- **One per occurrence, until the monitor expires (re-arm to continue)** ("tell me every time an ERROR line appears") → Monitor with an unbounded command (`tail -f`, `inotifywait -m`, `while true`).
+- **One per occurrence, until a known end** ("emit each CI step result, stop when the run completes") → Monitor with a command that emits lines and then exits.
+Your script's stdout is the event stream. Each line becomes a notification. Exit ends the watch.
+  # Each matching log line is an event
+  tail -f /var/log/app.log | grep --line-buffered "ERROR"
+  # Each file change is an event
+  inotifywait -m --format '%e %f' /watched/dir
+  # Poll GitHub for new PR comments and emit one line per new comment
+  last=$(date -u +%Y-%m-%dT%H:%M:%SZ)
+  while true; do
+    now=$(date -u +%Y-%m-%dT%H:%M:%SZ)
+    gh api "repos/owner/repo/issues/123/comments?since=$last" --jq '.[] | "\(.user.login): \(.body)"'
+    last=$now; sleep 30
+  done
+  # Node script that emits events as they arrive (e.g. WebSocket listener)
+  node watch-for-events.js
+  # Per-occurrence with a natural end: emit each CI check as it lands, exit when the run completes
+  prev=""
+  while true; do
+    s=$(gh pr checks 123 --json name,bucket)
+    cur=$(jq -r '.[] | select(.bucket!="pending") | "\(.name): \(.bucket)"' <<<"$s" | sort)
+    comm -13 <(echo "$prev") <(echo "$cur")
+    prev=$cur
+    jq -e 'all(.bucket!="pending")' <<<"$s" >/dev/null && break
+    sleep 30
+  done
+**Don't use an unbounded command for a single notification.** `tail -f`, `inotifywait -m`, and `while true` never exit on their own, so the monitor stays armed until timeout even after the event has fired. For "tell me when X is ready," use Bash `run_in_background` with an `until` loop instead (one notification, ends in seconds). Note that `tail -f log | grep -m 1 ...` does *not* fix this: if the log goes quiet after the match, `tail` never receives SIGPIPE and the pipeline hangs anyway.
+**Script quality:**
+- Every pipe stage must flush per line or matches sit in its buffer unseen: `grep` needs `--line-buffered`, `awk` needs `fflush()`. `head` cannot flush at all — `| head -N` delivers nothing until N matches accumulate, then ends the stream.
+- In poll loops, handle transient failures (`curl ... || true`) — one failed request shouldn't kill the monitor.
+- Poll intervals: 30s+ for remote APIs (rate limits), 0.5-1s for local checks.
+- Write a specific `description` — it appears in every notification ("errors in deploy.log" not "watching logs").
+- Only stdout is the event stream. Stderr goes to the output file (readable via Read) but does not trigger notifications — for a command you run directly (e.g. `python train.py 2>&1 | grep --line-buffered ...`), merge stderr with `2>&1` so its failures reach your filter. (No effect on `tail -f` of an existing log — that file only contains what its writer redirected.)
+**Coverage — silence is not success.** When watching a job or process for an outcome, your filter must match every terminal state, not just the happy path. A monitor that greps only for the success marker stays silent through a crashloop, a hung process, or an unexpected exit — and silence looks identical to "still running." Before arming, ask: *if this process crashed right now, would my filter emit anything?* If not, widen it.
+  # Wrong — silent on crash, hang, or any non-success exit
+  tail -f run.log | grep --line-buffered "elapsed_steps="
+  # Right — one alternation covering progress + the failure signatures you'd act on
+  tail -f run.log | grep -E --line-buffered "elapsed_steps=|Traceback|Error|FAILED|assert|Killed|OOM"
+For poll loops checking job state, emit on every terminal status (`succeeded|failed|cancelled|timeout`), not just success. If you cannot confidently enumerate the failure signatures, broaden the grep alternation rather than narrow it — some extra noise is better than missing a crashloop.
+**Output volume**: Every stdout line is a conversation message, so the filter should be selective — but selective means "the lines you'd act on," not "only good news." Never pipe raw logs; filter to exactly the success and failure signals you care about. Monitors that produce too many events are automatically stopped; restart with a tighter filter if this happens.
+Stdout lines within 200ms are batched into a single notification, so multiline output from a single event groups naturally.
+The script runs in the same shell environment as Bash. Exit ends the watch (exit code is reported). Every monitor expires after `timeout_ms` (default 5 minutes, at most 30 minutes): it is killed and you get one notice with the event count. Re-arm it if you still need the watch; for a long watch (PR monitoring, log tails) set `timeout_ms` to the maximum and re-arm on each expiry, and widen the filter if an expiry with no events was unexpected. Use TaskStop to cancel early.
+**ws source** — open a WebSocket and stream each incoming text frame as an event. No shell, no polling: the server pushes, you get notified.
+  Monitor({
+    ws: {url: 'wss://events.example.com/stream', protocols: ['v1']},
+    description: 'deploy events',
+  })
+Each text frame becomes one notification (multiline frames stay as one event). Binary frames are reported as `[binary frame, N bytes]` rather than passed through. Socket close ends the watch with the close code surfaced; errors are surfaced before close. Same rate limiting as bash — a firehose will be suppressed and eventually stopped, so subscribe to a filtered feed where one exists.
+Prefer this over `command: 'websocat wss://…'` — it avoids the extra process and line-buffering pitfalls. Use bash when you need to transform or filter frames with shell tools before they become events.

+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "additionalProperties": false,
+ "properties": {
+  "command": {
+   "description": "Shell command or script. Each stdout line is an event; exit ends the watch.",
+   "type": "string"
+  },
+  "description": {
+   "description": "Short human-readable description of what you are monitoring (shown in notifications).",
+   "type": "string"
+  },
+  "timeout_ms": {
+   "default": 300000,
+   "description": "Kill the monitor after this deadline. Default 300000ms. Deadlines above 600000ms are capped to 600000ms. You are notified at expiry and can re-arm.",
+   "maximum": 3600000,
+   "minimum": 1000,
+   "type": "number"
+  },
+  "ws": {
+   "additionalProperties": false,
+   "description": "WebSocket to open. Each text frame is an event; binary frames are reported as a placeholder line. Socket close ends the watch. Cannot be combined with command.",
+   "properties": {
+    "protocols": {
+     "items": {
+      "pattern": "^[!#$%&'*+.^_`|~0-9A-Za-z-]+$",
+      "type": "string"
+     },
+     "type": "array"
+    },
+    "url": {
+     "type": "string"
+    }
+   },
+   "required": [
+    "url"
+   ],
+   "type": "object"
+  }
+ },
+ "required": [
+  "description",
+  "timeout_ms"
+ ],
+ "type": "object"
+}
```

## 3. Bash blocks long leading sleeps and points polling to Monitor

Bash tool description · under “Instructions”

Bash no longer advises polling with a check command or keeping sleeps short. It now says long leading sleep commands are blocked, Claude should poll with Monitor using an until-loop, and Claude must not chain shorter sleeps to get around the block. It appeared in 2 of 6 captures.

- +45 / -33 words
- Landed in 2 of 23 prompts (main prompt 2/6)

```diff
-  - If you must poll an external process, use a check command (e.g. `gh run view`) rather than sleeping first.
-  - If you must sleep, keep the duration short to avoid blocking the user.
+  - Long leading `sleep` commands are blocked. To poll until a condition is met, use Monitor with an until-loop (e.g. `until <check>; do sleep 2; done`) — you get a notification when the loop exits. Do not chain shorter sleeps to work around the block.
```

## 4. CronCreate says to use Monitor, not cron, for live watching

CronCreate tool description · under “Durability”

Adds a note that CronCreate re-runs a prompt at fixed intervals. For watching logs, processes or command output and being notified as changes happen, Claude should use Monitor instead. It appeared in 2 of 6 captures.

- +46 / -0 words
- Landed in 2 of 23 prompts (main prompt 2/6)

```diff
+## Not for live watching
+CronCreate re-runs a prompt at fixed wall-clock intervals. To watch a log file, process, or command output and be notified the moment something changes, use the Monitor tool instead — Monitor streams events as they happen; cron polls on a schedule.
```

## 5. Bash points to Monitor for streaming background process events

Bash tool description · under “Instructions”

Adds a Bash instruction to use Monitor when streaming events from a background process, where each stdout line becomes a notification. For a one-shot wait until something finishes, Claude should keep using Bash with run_in_background. It appeared in 2 of 6 captures.

- +28 / -0 words
- Landed in 2 of 23 prompts (main prompt 2/6)

```diff
+  - Use the Monitor tool to stream events from a background process (each stdout line is a notification). For one-shot "wait until done," use Bash with run_in_background instead.
```

## 6. Bash run_in_background note says foreground sleep is blocked

Bash tool description · entry “`run_in_background` runs the command detached”

Adds to the run_in_background entry that foreground sleep is blocked and that Claude should wait on a condition using Monitor with an until-loop. The rest of the entry is unchanged. It appeared in 2 of 6 captures.

- +15 / -0 words
- Landed in 18 of 23 prompts (main prompt 2/6, session agents, slash commands 3/3, compaction, system reminders 8/8, subagents 3/3)

```diff
-- `run_in_background` runs the command detached: it keeps running across turns and re-invokes you when it exits. With it, `timeout` is how long the command may run in the background (default 600000, max 7200000); at that limit it is stopped and you are re-invoked. No `&` needed.
+- `run_in_background` runs the command detached: it keeps running across turns and re-invokes you when it exits. With it, `timeout` is how long the command may run in the background (default 600000, max 7200000); at that limit it is stopped and you are re-invoked. No `&` needed. Foreground `sleep` is blocked; use Monitor with an until-loop to wait on a condition.

-- `run_in_background` runs the command detached: it keeps running across turns and re-invokes you when it exits. No `&` needed. You will be notified when it exits — do not poll its output file while waiting.
+- `run_in_background` runs the command detached: it keeps running across turns and re-invokes you when it exits. No `&` needed. You will be notified when it exits — do not poll its output file while waiting. Foreground `sleep` is blocked; use Monitor with an until-loop to wait on a condition.
```

## 7. Monitor added to the listed tools in appended context

Appended context (skills, environment) · at “Monitor”

The word Monitor now appears in the appended context, matching the arrival of the new Monitor tool. It appeared in 2 of 6 captures.

- +1 / -0 words
- Landed in 18 of 23 prompts (main prompt 2/6, session agents, slash commands 3/3, compaction, system reminders 8/8, subagents 3/3)

```diff
+Monitor
```

## Where each change landed
- [Claude Code, default](https://changelogs.core-directive.com/prompts/2.1.310/system?arm=cli): #2, #3, #4, #5, #6, #7
- [Claude Code, seeded with this account's feature flags](https://changelogs.core-directive.com/prompts/2.1.310/system?arm=cli&config=flags-account): unchanged
- [Claude Code, seeded with anonymous feature flags](https://changelogs.core-directive.com/prompts/2.1.310/system?arm=cli&config=flags-anon): unchanged
- [Agent SDK, default](https://changelogs.core-directive.com/prompts/2.1.310/system?arm=sdk-cli): #2, #3, #4, #5, #6, #7
- [Agent SDK, seeded with this account's feature flags](https://changelogs.core-directive.com/prompts/2.1.310/system?arm=sdk-cli&config=flags-account): unchanged
- [Agent SDK, seeded with anonymous feature flags](https://changelogs.core-directive.com/prompts/2.1.310/system?arm=sdk-cli&config=flags-anon): unchanged
- [Auto-mode classifier](https://changelogs.core-directive.com/prompts/2.1.310/system?surface=automode&arm=sdk-cli): unchanged
- [Session agents: claude](https://changelogs.core-directive.com/prompts/2.1.310/system?surface=agent-claude&arm=cli): #6, #7
- [Slash commands: /init](https://changelogs.core-directive.com/prompts/2.1.310/system?surface=command-init&arm=sdk-cli): #6, #7
- [Slash commands: /security-review](https://changelogs.core-directive.com/prompts/2.1.310/system?surface=command-security-review&arm=sdk-cli): #6, #7
- [Slash commands: /team-onboarding](https://changelogs.core-directive.com/prompts/2.1.310/system?surface=command-team-onboarding&arm=sdk-cli): #6, #7
- [Compaction: Compaction instruction](https://changelogs.core-directive.com/prompts/2.1.310/system?surface=compaction&arm=cli): #6, #7
- [System reminders: Claude md](https://changelogs.core-directive.com/prompts/2.1.310/system?surface=reminder-claude-md&arm=sdk-cli): #6, #7
- [System reminders: Hook pretool block](https://changelogs.core-directive.com/prompts/2.1.310/system?surface=reminder-hook-pretool-block&arm=sdk-cli): #6, #7
- [System reminders: Read dup](https://changelogs.core-directive.com/prompts/2.1.310/system?surface=reminder-read-dup&arm=sdk-cli): #6, #7
- [System reminders: Read empty](https://changelogs.core-directive.com/prompts/2.1.310/system?surface=reminder-read-empty&arm=sdk-cli): #6, #7
- [System reminders: Read offset](https://changelogs.core-directive.com/prompts/2.1.310/system?surface=reminder-read-offset&arm=sdk-cli): #6, #7
- [System reminders: Skill run](https://changelogs.core-directive.com/prompts/2.1.310/system?surface=reminder-skill-run&arm=sdk-cli): #6, #7
- [System reminders: Todo nag](https://changelogs.core-directive.com/prompts/2.1.310/system?surface=reminder-todo-nag&arm=sdk-cli): #6, #7
- [System reminders: Toolsearch deferred](https://changelogs.core-directive.com/prompts/2.1.310/system?surface=reminder-toolsearch-deferred&arm=sdk-cli): #1, #2, #6, #7
- [Subagents: explore](https://changelogs.core-directive.com/prompts/2.1.310/system?surface=subagent-explore&arm=sdk-cli): #6, #7
- [Subagents: general-purpose](https://changelogs.core-directive.com/prompts/2.1.310/system?surface=subagent-general-purpose&arm=sdk-cli): #6, #7
- [Subagents: plan](https://changelogs.core-directive.com/prompts/2.1.310/system?surface=subagent-plan&arm=sdk-cli): #6, #7

## Did not change
- 28 tool descriptions are word for word the same, 3 moved and are listed above. Unchanged: Agent, AskUserQuestion, CronDelete, CronList, DeferredToolPlaceholder, Edit, EnterPlanMode, EnterWorktree, ExitPlanMode, ExitWorktree, ListAgents, NotebookEdit, PushNotification, Read, ReportFindings, ScheduleWakeup, SendMessage, Skill, TaskCreate, TaskGet, TaskList, TaskStop, TaskUpdate, ToolSearch, WebFetch, WebSearch, Workflow, Write.
- 5 prompts did not move at all: Claude Code, seeded with this account's feature flags, Claude Code, seeded with anonymous feature flags, Agent SDK, seeded with this account's feature flags, Agent SDK, seeded with anonymous feature flags, Auto-mode classifier
- set aside as capture noise: 358 billing header lines
- set aside as capture noise: 224 capture date lines
- set aside as capture noise: 224 host kernel lines
