monitor-patterns

star 0

Monitor primitive usage patterns for streaming command output as discrete events. Provides decision matrix (Monitor vs run_in_background vs raw Bash), canonical helper signatures (monitor_turbo_stream, monitor_vercel_deploy, monitor_gh_ci, monitor_gh_comments, monitor_lock_files), before/after migration patterns, and anti-patterns. Use when running long-lived commands that produce per-line events — Playwright suites, Vercel deploys, CI runs, PR bot comments, file-watch signals — and you need to react per-event instead of waiting for batch completion. Triggers: Playwright test execution, deploy state watching, CI completion polling, bot review polling, multi-line stdout streaming.

leonardoacosta By leonardoacosta schedule Updated 6/4/2026

name: monitor-patterns description: "Monitor primitive usage patterns for streaming command output as discrete events. Provides decision matrix (Monitor vs run_in_background vs raw Bash), canonical helper signatures (monitor_turbo_stream, monitor_vercel_deploy, monitor_gh_ci, monitor_gh_comments, monitor_lock_files), before/after migration patterns, and anti-patterns. Use when running long-lived commands that produce per-line events — Playwright suites, Vercel deploys, CI runs, PR bot comments, file-watch signals — and you need to react per-event instead of waiting for batch completion. Triggers: Playwright test execution, deploy state watching, CI completion polling, bot review polling, multi-line stdout streaming." user-invocable: false allowed-tools: Read, Glob, Grep, Bash

Monitor Patterns

The Monitor primitive wraps a shell command and treats each stdout line as an event notification. Use it when the problem is "tell me every time X happens" — not "wait until X is done" (that's run_in_background: true).

Every Monitor invocation MUST use a selective filter (grep --line-buffered, jq, awk, or a helper function) so only discrete events reach the chat. Raw log tails auto-stop.

Decision Matrix

Need Primitive Why
Run a Playwright suite, react to each PASS/FAIL/SKIP Monitor + filter Per-test events, no buffering, low context cost
Wait for one test run to finish (no streaming) Bash({ run_in_background: true }) Single completion signal, no per-event work
Tail Vercel deploy state during CI/E2E setup Monitor + monitor_vercel_deploy Discrete READY/ERROR transitions
Watch CI for terminal state Monitor + monitor_gh_ci Final-state-only event stream
Watch PR for bot review comments Monitor + monitor_gh_comments Per-comment events
Watch a cooperative signal directory Monitor + monitor_lock_files inotify + ls fallback
One-shot diagnostic command (playwright test --list, bd show) Raw Bash No streaming needed
Native blocking command with clean exit (gh run watch --exit-status) Raw Bash Already does the right thing

Canonical Helpers

Source the library in any command or agent — never reinvent poll loops:

source ~/dev/cc/scripts/lib/monitor-helpers.sh
Helper Signature Purpose
monitor_turbo_stream TURBO_ARGS... Per-package / per-spec completion lines from a turbo or test run
monitor_vercel_deploy PROJECT BRANCH [POLL_SECONDS] READY / ERROR transitions on a deployment
monitor_gh_ci BRANCH [POLL_SECONDS] CI terminal state for a branch
monitor_gh_comments PR_NUMBER BOT_LOGIN [POLL_SECONDS] Stream bot review comments as they post
monitor_lock_files LOCK_DIR PATTERN_REGEX inotify-based file-watch with ls fallback

Defaults and edge cases live inside the helpers. Read ~/dev/cc/scripts/lib/monitor-helpers.sh for full signatures.

Three Canonical Patterns

1. Filtered-stream — wrap a streaming tool, grep for terminal lines

The most common e2e/test pattern. Replaces raw pnpm playwright test invocations.

Before (raw Bash — blocks, dumps all stdout):

cd packages/e2e && pnpm with-env playwright test "$TEST_PATH" 2>&1

After (Monitor — per-spec completion events):

source ~/dev/cc/scripts/lib/monitor-helpers.sh
monitor_turbo_stream pnpm --filter @oo/e2e playwright test "$TEST_PATH" --reporter=line

The agent sees [wave-2] ✓ tests/foo.spec.ts:42 (1.2s) per pass and [wave-2] ✗ tests/bar.spec.ts:18 per fail — each line is one event, not a buffered dump.

2. Shell-poll — internal loop polling a remote API

Used for cloud state where there's no native streaming. Helpers like monitor_vercel_deploy and monitor_gh_ci implement this internally.

source ~/dev/cc/scripts/lib/monitor-helpers.sh
monitor_vercel_deploy otaku-odyssey dev 10  # poll every 10s, emit READY/ERROR

The wrapped helper polls the Vercel API and emits one line per state transition. The agent reacts to the READY event by triggering downstream work, or to ERROR by surfacing the failure.

3. File-watch — inotifywait -m on a cooperative signal dir

Used when commands write completion markers to a shared directory.

source ~/dev/cc/scripts/lib/monitor-helpers.sh
monitor_lock_files "/tmp/wave-locks" '^wave-[0-9]+\.done$'

Each new lock file matching the regex emits one event. Used by /plan:strategy Phase 3 fan-out gate.

Poll-Loop Exit Anti-Pattern: never gate on a strict parser whose error is swallowed

A shell-poll helper (Pattern 2) breaks out of its while loop when a status field reaches a terminal value. If the variable that gates the break/return is extracted with a strict parser (jq) whose error is swallowed (2>/dev/null), the loop silently never exits — it runs to its iteration cap (tens of minutes) instead of stopping on completion.

Root cause (observed against Azure DevOps): ADO build / timeline JSON embeds raw control chars (U+0000U+001F) inside string fields — the triggering commit message, triggerInfo, log URLs, and issue messages. jq is strict and errors on unescaped control chars. With ... | jq -r '.status' 2>/dev/null the error is discarded, the gating variable becomes empty (""), the terminal-state comparison never matches, and the loop polls forever.

Tell-tale signature: every progress line renders the gated field as empty (e.g. / :: where a status should be), and the stream ends with no terminal (READY/succeeded/FAILED) line — the Monitor lifetime expires instead.

Fix — extract the gate scalar robustly. Two idioms, in order of preference:

# A. grep-extract the one scalar the loop gates on (control-char-proof, no parser)
bstatus=$(printf '%s' "$body" | grep -o '"status":"[^"]*"' | head -1 | cut -d'"' -f4)

# B. python json.loads(strict=False) — when you need structured fields, not just one scalar
state=$(printf '%s' "$body" | python3 -c \
  'import sys,json; d=json.load(sys.stdin, strict=False); print(d.get("result") or d.get("status") or "")')

Rules:

  • NEVER pipe raw ADO (or any control-char-bearing) JSON through jq to produce the variable a loop's exit depends on.
  • NEVER 2>/dev/null-swallow the parser whose output gates the loop — a swallowed failure on the gate scalar is an infinite loop. (Swallowing is fine on a parser that only formats an event line; the loop's exit does not depend on it.)
  • The robust-extract idiom belongs in the helper, not the caller — see monitor_azure_pipeline in ~/dev/cc/scripts/lib/monitor-helpers.sh, which gates its terminal-state return on a python json.loads(strict=False)-extracted state (idiom B — it pulls the precise run fields .[0].result/.id/._links.web.href, control-char-proof) so it cannot hang on a jq parse error.

When NOT to Use Monitor

  • One-shot lookups (playwright test --list, bd show, git log, gh pr view) — Monitor's per-line semantics add no value; raw Bash with stdout capture is correct
  • Native-blocking commands with clean exit codes (gh run watch --exit-status) — already does the right thing; wrapping in Monitor adds latency without benefit
  • Parallel Agent dispatches — Monitor cannot observe sub-agent completion; the orchestrator coordinates fan-in via task results, not stdout streaming
  • Non-streaming commands — anything that produces output as one batch at the end (e.g. a curl response) — run_in_background: true is the right primitive

Reference Sites

Production usage to copy from:

Command / Agent Monitor usage
/p2p Step 3 bot resolvers monitor_gh_comments per bot login
/apply:all Step 4.7 deploy gate monitor_vercel_deploy (Vercel)
/test:test Step 4d CI poll monitor_gh_ci
/plan:strategy Phase 3 fan-out monitor_lock_files
/test:run-quality-gates monitor_turbo_stream per gate
/test:e2e Phase 11 triage dispatch Agent fan-out (NOT Monitor — sub-agent completion not observable via stdout)

Cross-Reference

Full primitive specification in ~/dev/cc/rules/TOOLING.md § Monitor Tool Pattern. This skill is the agent-facing summary — the rules file is the canonical contract.

Install via CLI
npx skills add https://github.com/leonardoacosta/central-claude --skill monitor-patterns
Repository Details
star Stars 0
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator
leonardoacosta
leonardoacosta Explore all skills →