Receber por email
← Voltar para o catálogo

monitor-events

Token-efficient Claude Code workspace with parallel agents and persistent memory. Research → Plan → Implement → Validate workflow.

18estrelas
Atualizado há 3 meses

Ver no GitHub ↗

Como adicionar

/plugin marketplace add coalesce-labs/catalyst

O comando exato pode variar conforme o repositório. Confira o README no GitHub.

Para o autor da skill

Cole no README do seu repo

Mostra que sua skill está catalogada na Skillteca, gera backlink e tráfego rastreável.

[![Skillteca](https://www.skillteca.com.br/api/badge/monitor-events/svg)](https://www.skillteca.com.br/skills/monitor-events)

Skills relacionadas

Ver todas de Pesquisa e Web →

Alerta por categoria

Receba novas skills de Pesquisa e Web toda segunda

1 email curto, só com as skills novas de Pesquisa e Web. 4 minutos de leitura, sem spam, cancela com 1 clique.

Você confirma o email no primeiro envio. Sem spam. Cancela com 1 clique.

Ler descrição completa

monitor-events — Event-driven waits in skill prose

What this is for

CTL-210 unified the Catalyst event log: every GitHub webhook, Linear webhook, comms post, and orchestrator/worker lifecycle event flows through ~/catalyst/events/YYYY-MM.jsonl. Consumers no longer poll gh pr view, linearis read, or signal files — they subscribe to the event stream via filter.

This skill documents the canonical patterns. Use it as a reference when writing or migrating skill prose; do not invoke it as a slash command.

Prerequisite — orch-monitor daemon must be running

The two primitives below read from ~/catalyst/events/YYYY-MM.jsonl, which is populated by the orch-monitor daemon (plugins/dev/scripts/orch-monitor/server.ts). When the daemon is not running:

  • catalyst-events tail returns an empty stream
  • catalyst-events wait-for blocks until its --timeout expires (default 600s) and

exits non-zero — callers fall back to gh pr view polling, which can't see deploys

Liveness check (the same call wired into check-project-setup.sh):

plugins/dev/scripts/catalyst-monitor.sh status        # human-readable
plugins/dev/scripts/catalyst-monitor.sh status --json # {"running":true,"pid":...}

Skills that invoke check-project-setup.sh (orchestrate, oneshot, merge-pr) handle the liveness check automatically — interactive runs prompt to start the daemon, autonomous runs warn-to-stderr and proceed. If you reuse the primitives outside those skills, run the status check yourself and either start the daemon (catalyst-monitor.sh start) or plan for the polling fallback.

Pattern selection & cost tradeoffs

Three patterns are available; pick by cost shape, not just by mechanism. Listed cheapest first.

| Pattern | Mechanism | Cost shape | When to use | |---|---|---|---| | Broker interest (preferred) | The catalyst-broker daemon (see [[broker]]) classifies events between Claude turns and emits filter.wake.{id} only on semantic match. Deterministic interest types (pr_lifecycle, ticket_lifecycle, comms_lifecycle) match by typed-field comparison; prose interests go through Groq Llama 3.1 8B. | Lowest. Zero turns while blocked; 1 turn per matched wake. Deterministic routes cost 0 LLM tokens; prose routes ~$0.05–0.10/M tokens at small batch sizes. Pre-filtering happens out of Claude's context entirely. | Whenever the broker daemon is running. Worker-scope (single PR) and orchestrator-scope (many PRs) both supported via the same registration mechanism. | | catalyst-events wait-for with jq | Blocking Bash CLI; one jq predicate; exits on first match or --timeout. Works without the broker. | Zero turns while blocked, 1 turn per match. Same per-match cost as Monitor but workers usually wait for ONE specific event, so total turn count stays low. Filter expansion (CI + reviews + push + merge) widens the per-match probability. | Short-lived claude -p workers when the broker is not running; standalone one-shot waits; CI scripts. | | Monitor over catalyst-events tail | Claude Code's Monitor tool wraps tail --filter; every matching line surfaces as a turn-resuming notification. | Highest. 1 wake per matching line means broad filters can dominate context. | Long-lived orchestrator only. NOT for short-lived claude -p workers — they have no long-lived turn loop to consume notifications. |

Worker contract (matches oneshot Phase 5, CTL-371): dispatched workers prefer the broker
(Pattern 3), fall back to wait-for (Pattern 2) when the daemon is down, and never use
Monitor/tail (Pattern 1). See plugins/dev/skills/oneshot/SKILL.md Phase 5 and the
orchestrate dispatch prompt for the canonical invocation.
Note on numbering. The "Pattern N" labels in the recipe sections below (Pattern 1 — worker
waits for PR merge, Pattern 2 — long-lived orchestrator wakes, Pattern 3 — reactive PR lifecycle,
Pattern 4 — tail by ticket) are recipe IDs, not the cost-tier rank in the table above. The
recipes pre-date the broker integration; both the broker-preferred path and the wait-for
fallback inside each recipe map to the table rows above.

Both wait-for and Monitor use catalyst-events under the hood. tail is the streaming foundation; wait-for is tail | head -n 1 with a timeout. The broker reads the same event log as tail does but classifies before waking, which is why its per-wake cost is so much lower.

Pattern 1 — Worker waits for its PR to merge

A claude -p worker that just opened PR #342 needs to block until the PR merges, then do post-merge work.

Preferred (when catalyst-filter is running, CTL-269): register a single semantic interest covering every concern the worker cares about (CI, comms, reviews, BEHIND, Linear), then wait on filter.wake.${CATALYST_SESSION_ID}. The Groq-backed daemon classifies raw events against the natural-language prompt and emits one wake per match. See [[catalyst-filter]] for the full registration recipe and the daemon-restart contract. The two-phase pattern below is the fallback for environments where the daemon is not running.

Use the two-phase pattern from [[wait-for-github]]: a 3-minute Phase 1 with a diagnostic checkpoint before committing to the full 2-hour wait.

# Two-phase pattern — see [[wait-for-github]] for full reference.
REPO=$(gh repo view --json nameWithOwner --jq '.nameWithOwner')
EVENT=""
_WFG_MATCHED=false

# Phase 1: short wait with diagnostic checkpoint (3 minutes).
EVENT=$(catalyst-events wait-for \
  --filter ".attributes.\"event.name\" == \"github.pr.merged\" and .attributes.\"vcs.pr.number\" == ${PR_NUMBER}" \
  --timeout 180 2>/dev/null || true)

if [ -n "$EVENT" ]; then
  _WFG_MATCHED=true
else
  # Phase 1 timed out — run diagnostics before extending to Phase 2.
  echo "Phase 1 timed out after 3 min — running diagnostics..."
  STALLED=false
  FILTER_MISMATCH=false

  _LOG_FILE=~/catalyst/events/$(date -u +%Y-%m).jsonl
  _LOG_LINES=$(wc -l < "$_LOG_FILE" 2>/dev/null | tr -d ' ')
  _SINCE_LINE=$(( ${_LOG_LINES:-0} > 500 ? ${_LOG_LINES:-0} - 500 : 0 ))
  HEARTBEATS=$(catalyst-events tail --since-line "$_SINCE_LINE" 2>/dev/null \
    | jq -c 'select(.attributes."event.name" == "session.heartbeat")' | wc -l | tr -d ' ')
  [ "${HEARTBEATS:-0}" -eq 0 ] && { echo "WARN: No heartbeats — event log may be stalled"; STALLED=true; }

  RAW_HIT=$(catalyst-events tail --since-line "$_SINCE_LINE" 2>/dev/null | jq -c \
    --argjson pr "$PR_NUMBER" \
    'select((.attributes."vcs.pr.number" == $pr) or (.body.payload.prNumbers // [] | contains([$pr])))' | head -1)
  if [ -n "$RAW_HIT" ]; then
    echo "WARN: Event arrived but filter did not match. Raw event:"; echo "$RAW_HIT" | jq .
    FILTER_MISMATCH=true
  fi

  # The smee→monitor webhook tunnel is the GitHub-event ingestion path and is NOT yet
  # retired (Linear smee retires first; GitHub smee is gated on CTC-134). A dead tunnel
  # produces zero events while the monitor keeps heartbeating — so without this check a
  # worker would treat infra as healthy and enter the 2-hour Phase 2 wait. Tunnel down →
  # skip the extension and rely on the authoritative REST confirmation below.
  TUNNEL_STATE=$(catalyst-monitor status --json 2>/dev/null | jq -r '.webhookTunnel.connected // false')
  [ "$TUNNEL_STATE" != "true" ] && { echo "WARN: Webhook tunnel not running"; STALLED=true; }

  if [ "$FILTER_MISMATCH" = "false" ] && [ "$STALLED" = "false" ]; then
    # Infrastructure healthy — extend to Phase 2.
    EVENT=$(catalyst-events wait-for \
      --filter ".attributes.\"event.name\" == \"github.pr.merged\" and .attributes.\"vcs.pr.number\" == ${PR_NUMBER}" \
      --timeout 7200 2>/dev/null || true)
    [ -n "$EVENT" ] && _WFG_MATCHED=true
  fi
fi

# Authoritative REST confirmation — always follows any wait-for path.
MERGED=$(gh api "repos/${REPO}/pulls/${PR_NUMBER}" --jq '.merged' 2>/dev/null || echo "false")
if [ "$MERGED" = "true" ]; then
  # Proceed 
Compartilhar X LinkedIn