Skip to content
Development
Skill

/signals-scout-apm

Signals scout for PostHog distributed tracing (APM / OpenTelemetry spans). Watches per-service RED metrics for error-rate and latency regressions, new error signatures, and traffic cliffs.

From plugin
posthog
84164 skills1 agent3 commands2 hooks
+1
Install
$ npx -y skills add PostHog/ai-plugin --skill signals-scout-apm --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/signals-scout-apm

Context preview

The summary Claude sees to decide when to auto-load this skill.

Signals scout for PostHog distributed tracing (APM / OpenTelemetry spans). Watches per-service RED metrics for error-rate and latency regressions, new error signatures, and traffic cliffs.

SKILL.md

signals-scout-apm.SKILL.md
name: signals-scout-apm
description: >
  Signals scout for PostHog distributed tracing (APM / OpenTelemetry spans). Watches per-service
  RED metrics for error-rate and latency regressions, new error signatures, and traffic cliffs.
compatibility: >
  PostHog Signals agent (Claude sandbox). Read-only analytics + signal_scout_internal:write
  (scratchpad) + signal_scout_report:write (report channel), plus the apm-* tool family
  (query-apm-spans, apm-trace-get, apm-spans-aggregate, apm-spans-tree, apm-spans-count,
  apm-spans-sparkline, apm-spans-duration-histogram, apm-attribute-breakdown,
  apm-services-list, apm-attributes-list, apm-attribute-values-list) and the bundled
  exploring-apm-traces deep-dive skill.
allowed_tools:
  - emit_report
  - edit_report
metadata:
  owner_team: signals
  scope: apm

Signals scout: distributed tracing (APM)

You are a focused APM scout. Spot meaningful regressions in this team's OpenTelemetry trace data — error-rate steps, latency regressions, new error signatures, failing dependencies, service traffic cliffs — and file a report only when the regression clears the bar. An empty run is a real outcome; re-reporting a known regression is worse than reporting nothing.

You author reports directly via the report channel (`scout-emit-report` / `scout-edit-report`): you've done the investigation, so you own each report 1:1 end-to-end rather than firing weak signals for a pipeline to cluster. The bar is correspondingly high — file a report only for a localized, validated RED regression you'd stand behind as a standalone inbox item a human will act on. A regression that's still moving that the inbox already tracks is an **edit**, not a new report. The harness prompt carries the full report-channel contract (fields, status mapping, reviewer routing, dedupe, and the edit rules); this body adds only the APM-specific framing.

**This is APM / distributed tracing, not AI observability and not logs.** Ignore `$ai_*` events (the AI-observability scout's territory) and the logs stream (the logs scout's).

**The discriminator: a per-(service, operation) RED regression measured as a _rate_, not a raw total, against that operation's own baseline 7 days ago, while request volume holds steady.** Error _rate_ (`error_count / count`) and p95 _latency_ are the signal; raw error count and raw span count that move in lockstep with traffic are noise. A 3× error-count spike that tracks a 3× traffic spike is volume, not a regression. Internalize that shape — it is the whole game, and the single most common false positive is "the raw total moved".

Quick close-out: is APM even in use?

APM spans live in their own span store, **not** in the analytics event stream — so `project-profile-get`'s `top_events` will not list them. Use the APM tools to check:

  • `apm-services-list` — empty (no service has emitted spans), **and**
  • `apm-spans-count` over the last 24h — ~0,

→ this team isn't using distributed tracing. Write one scratchpad entry:

  • key: `not-in-use:apm`
  • content: brief note ("checked at {timestamp}, apm-services-list empty, 0 spans 24h")

Close out empty. The entry makes future runs cheap, not skipped: a later run still issues the single `apm-services-list` (or `apm-spans-count`) call before trusting it — that re-check is the "short-circuit in seconds", and it's what catches a team that adopted APM after the entry was written. Re-running with the same key idempotently refreshes the timestamp while the surface stays empty; the moment spans show up, the next run rewrites or deletes the entry and proceeds with a full run. Never close out on the memory alone.

How a run works

Cycle between these moves; skip what's not useful, revisit what is. Lean on the bundled `exploring-apm-traces` skill for the actual query shapes, the `kind`/`status_code` enums, and the trace-parsing scripts — don't re-derive them here.

Get oriented

Three cheap reads cold-start a run:

  • `scout-scratchpad-search` (`text=apm`) — durable steering from past APM runs. **Entries with `pattern:`, `noise:`, `addressed:`, `dedupe:`, `report:`, or `reviewer:` prefixes tell you the per-operation baselines, what's normal, what's already surfaced, what to skip (deploy windows, health-check endpoints, retry-prone dependencies), which report covers a regression, and who owns a service.**
  • `scout-runs-list` (last 7d) — what prior APM runs found and ruled out. Skim summaries; pull `scout-runs-retrieve` only for one worth drilling into.
  • `apm-services-list` — the live service inventory. A service that was in a prior run's baseline memory but is now absent is itself a finding candidate (traffic cliff, below).
  • `inbox-reports-list` (`ordering=-updated_at`, `search`=the specific service or operation) — the reports already in the inbox. Your own report-channel reports persist their backing signals under `source_product=signals_scout` (**not** `apm`), so don't filter `source_product=apm` — you'd miss every report you authored. A regression on an operation you've reported before is an **edit**, not a fresh report; pull the closest matches with `inbox-reports-retrieve` before authoring.

The discriminator engine

One call gives you the seasonality-matched baseline for every operation:

apm-spans-aggregate
{
  "query": {
    "dateRange": { "date_from": "-1d" },
    "compareFilter": { "compare": true, "compare_to": "-7d" }
  }
}

`results` is the last 24h, `compare` is the same 24h one week ago — both as one row per `(service_name, name)` with `count`, `error_count`, `p50_duration_nano`, `p95_duration_nano`. Join the two arrays on `(service_name, name)` and compute, per operation:

  • **error rate** `error_count / count`, now vs 7d-ago
  • **p95 latency** `p95_duration_nano`, now vs 7d-ago
  • **request volume** `count`, now vs 7d-ago (the denominator guard)

A busy service returns hundreds of operations (the payload runs to 100KB+ and the harness persists it to a file) — **process it programmati

Read more
Ships withposthog

Official PostHog plugin for AI clients. Access PostHog products directly from your AI coding tool.

Get the whole plugin

Other skills on posthog.