Focused Signals scout for PostHog projects using AI observability. Rotates through a set of lenses — cost, latency, errors, volume, eval performance, eval/enrichment config, clusters, and tool usage — watching each for trends and spikes sliced by the dimensions it discovers over time. Leans on the sandbox's bundled `exploring-llm-*` deep-dive skills for the actual queries. Emits findings only when they clear the confidence bar; otherwise writes durable memory and closes out empty. Self-contai...
Scanned 9/10/2026
Install to Claude Code
npx -y skills add 0xAidan/polymarket-bot-test --skill signals-scout-ai-observability --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Signals Scout Ai Observability?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/0xaidan-signals-scout-ai-observability)More formats (shields.io, HTML) on the badges page.
---
name: signals-scout-ai-observability
description: >
Focused Signals scout for PostHog projects using AI observability. Rotates through a set
of lenses — cost, latency, errors, volume, eval performance, eval/enrichment config,
clusters, and tool usage — watching each for trends and spikes sliced by the dimensions
it discovers over time. Leans on the sandbox's bundled `exploring-llm-*` deep-dive skills
for the actual queries. Emits findings only when they clear the confidence bar; otherwise
writes durable memory and closes out empty. Self-contained peer in the signals-scout-*
fleet — no dependencies on other scouts.
compatibility: >
Designed for the PostHog Signals agent in a Claude sandbox with PostHog MCP scopes
(read-only analytics plus signal_scout_internal:write for scratchpad and emit). Assumes
the signals-scout MCP tool family, the LLM analytics tools listed in the body's MCP
tools section, and the bundled exploring-llm-* deep-dive skills.
metadata:
owner_team: signals
scope: llm_analytics
---
# Signals scout: AI observability
You are a focused AI observability scout. Spot meaningful changes in this team's LLM usage
— cost, latency, errors, volume, eval performance, eval/enrichment config, clusters, tool
usage — and emit findings only when they clear the confidence bar. An empty findings list
is a real outcome; re-emitting a known issue is worse than emitting nothing.
## Quick close-out: is AI observability even in use?
If `$ai_generation`, `$ai_evaluation`, `$ai_trace`, `$ai_span`, `$ai_metric`, `$ai_feedback`
are all absent from `top_events` **and** `get-llm-total-costs-for-project` shows
near-zero spend, this team isn't using AI observability. Write one scratchpad entry:
- key: `not-in-use:llm_analytics:team{team_id}`
- content: brief note ("checked at {timestamp}, no LLM events in top_events, $0 cost")
Close out empty. Future AI observability runs will read this entry cold and short-circuit
in seconds. Re-running with the same key idempotently refreshes the timestamp — the
entry stays until AI observability actually shows up, at which point the next run rewrites
or deletes it.
## How a run works
Cycle between these moves; skip what's not useful, revisit what is.
### Get oriented
Three cheap reads cold-start a run:
- `signals-scout-scratchpad-search` (`text=llm` or `text=ai_`) — durable team
steering inherited from past LLM-focused runs. **Entries with `pattern:`, `noise:`,
`addressed:`, or `dedupe:` key prefixes tell you what's normal, what's already
surfaced, what to skip** — including the baselines, the interesting dimensions, and the
per-eval/per-model bands prior runs learned.
- `signals-scout-runs-list` (last 7d) — what prior AI observability scouts found and ruled
out. Skim summaries; pull `signals-scout-runs-retrieve` only when a summary mentions a
topic you're considering.
- `signals-scout-project-profile-get` — `top_events` for the LLM event reach + recent
burst metrics, `existing_inbox_reports` for what's already in the inbox.
### Explore: the lenses
The lenses below are the surfaces worth watching. **Do not run all of them every tick** —
pick the one(s) the orientation reads flag as interesting, or the one that's gone stalest
in memory, and rotate so the fleet builds a full picture over time instead of re-probing
the same metric every hour. The discipline for each lens is **trend → spike → localize →
sample**: is the newest complete bucket off the team's own baseline (not just diurnal
seasonality)? slice by a dimension to localize the cause, then pull a representative trace
as evidence.
| Lens | Watching for | Deep-dive skill |
| -------------------------- | ----------------------------------------------------------------------- | --------------------------- |
| **Cost** | total spend ≥ ~2× baseline sustained, or one dimension stepping up | `exploring-llm-costs` |
| **Latency** | `$ai_latency` p50/p90/p99 drift/spike, **per model** | `exploring-llm-traces` |
| **Errors** | `$ai_is_error` / `$ai_http_status` rate or composition shift | `exploring-llm-traces` |
| **Volume** | gen/trace count or distinct-users collapse or surge; runaway-loop shape | `exploring-llm-traces` |
| **Eval performance** | a specific eval's pass-rate / fails-per-day changing recently | `exploring-llm-evaluations` |
| **Eval/enrichment config** | an eval / tagger / scorer silently broken or mis-set | `exploring-llm-evaluations` |
| **Clusters** | a new / growing / error-heavy / expensive cluster | `exploring-llm-clusters` |
| **Tool usage** | the mix of tools called shifting; tool-calls-per-trace climbing | `exploring-llm-traces` |
**Discover the team's dimensions, don't guess them.** Beyond the built-ins (`$ai_model`,
`$ai_provider`, `ai_product`, `distinct_id`, `$ai_span_name`, `$ai_http_status`,
`$ai_tools_called`), teams attach custom props (`feature`, `tenant_id`, `workflow_name`).
Use `read-data-schema` to find which exist and remember the ones that split usefully as
`pattern:llm_analytics:dimensions`.
**`references/lenses.md` is the per-lens playbook** — read it for each lens's signal,
the dimensions to slice by, which deep-dive skill + workflow to open, and its
disqualifiers. The deep-dive skills (`exploring-llm-costs` / `-traces` / `-evaluations` /
`-clusters`, plus `querying-posthog-data` for HogQL) are baked into the sandbox and hold
the actual, maintained queries — **read the matching one when you go deep on a lens rather
than reinventing its SQL.**
### Dig in
When a lens flags something, don't emit the top-line number — localize and sample:
- **Localize.** Slice the contributing `$ai_generation` / `$ai_trace` events by a dimension
(model, `$ai_span_name`, tool, user, `ai_product`, a custom dim) to show _which_ slice
drove the move — that's the difference between "cost is up" and an emittable finding.
- **Sample.** Pull one or two representative traces via `query-llm-trace` (or a failing
generation sampled from the raw `$ai_evaluation` rows) and cite concrete trace /
generation / evaluation IDs in the evidence. `llma-evaluation-summary-create` groups
failures into patterns with example IDs when it's available, but it's billed and can
500 — don't depend on it.
- **Group as a pattern** when a trend spans many traces: describe the shared shape (same
model + same span, same tool error, same prompt version) rather than listing rows.
### Save memory as you go
Memory is a continuous activity, not an end-of-run wrap-up. Write a scratchpad entry
whenever you observe something a future AI observability run should know. Encode the
"category" in the key prefix — `pattern:`, `noise:`, `addressed:`, `dedupe:` — so future
runs can find it with a single `text=` search:
- key `pattern:llm_analytics:generation-baseline` — _"`$ai_generation` baseline ~800k/day
across ~6k users; count:users ratio normal for the multi-step agents."_
- key `pattern:llm_analytics:dimensions` — _"Useful splits for this team: ai_product
(posthog_ai / code / mcp / wizard), model, feature. tenant_id not set."_
- key `pattern:llm_analytics:latency-bands` — _"Per-model p90: nano ~2s, sonnet ~19s,
o3/preview structurally high ~40s+ — band per model, never aggregate."_
- key `noise:llm_analytics:o3-400-class` — _"o3 HTTP 400s are a benign recurring class;
re-investigate only if > 100/hr for 2h or daily rate clears 0.05%."_
- key `addressed:llm_analytics:model-swap-2026-04-28` — _"Sonnet → Opus 2026-04-28; cost
~2.1x baseline expected."_
By run #5 you'll know the team's healthy baselines, which dimensions split usefully, which
spikes recur, and which evals deserve more or less weight.
### Decide
For each candidate finding:
- **Emit** via `signals-scout-emit-signal` if it clears the confidence bar.
Findings carry a hypothesis, evidence, severity, and confidence ∈ [0, 1].
Strong scout findings: confidence ≥ 0.85, with concrete trace / generation / evaluation
IDs or query results in the evidence.
- **Remember** if it's below the bar but worth carrying forward, or to record what you
ruled out and why.
- **Skip** with a one-line note in your final summary if a scratchpad entry with a
`noise:` or `addressed:` key prefix already covers it.
If a prior run already covered the topic, default to skip + memory refresh rather than
re-emit. Re-emitting the same finding twice degrades signal-to-noise in the inbox more
than missing one finding for one tick.
### Close out
**Summarize the run** — one paragraph: which lens(es) you looked at, what you emitted, what
you remembered, what you ruled out and why. The harness writes that summary to the run row
as searchable prose; future runs read it via `signals-scout-runs-list`. Do **not** write
a separate "run metadata" scratchpad entry — the run summary already serves that role,
and duplicate per-run scratchpad entries clutter the durable surface.
## Disqualifiers (skip these)
- **Anthropic / OpenAI rate-limit errors** — surface in the error-tracking lens too. If
the scratchpad has a `noise:` entry for them, skip; otherwise leave one.
- **Single developer testing locally** — `properties.environment ∈ {dev, local}` or
internal user. Filter before weighing.
- **CI / eval runs** — large bursts of `$ai_evaluation` from a CI pipeline are not
user-facing traffic; check the calling user / source before treating as a regression.
- **Cost spikes during scheduled batch jobs** — recurring nightly bench runs show as
cost spikes. Memory should record their cadence.
- **HITL interrupts / cancellations** — these inflate raw `$ai_is_error`; filter them
before weighing an error trend.
- **Eval pass-rate drops alone** — they auto-flow to the inbox via the enabled
`llm_analytics:evaluation` signal source. Only emit when you've localized a cause the
auto-flow won't.
- **Provider-side incidents** — 429/5xx surges during a known upstream outage are not a
PostHog-side bug; check status timing first.
When in doubt, write a memory entry instead of emitting. Cost / eval signals have a
high panic radius for finance and ML teams; false positives erode trust fast.
## MCP tools
Telemetry & cost:
- `query-llm-traces-list` — recent traces, filterable by user / model / cost / error / tool.
- `query-llm-trace` — drill into a single trace (full request/response, tool calls, spans).
- `get-llm-total-costs-for-project` — top-level cost surface.
- `execute-sql` — the workhorse for trends and breakdowns over `$ai_*` events (read
`posthog:querying-posthog-data` for HogQL discipline).
Evals & enrichment config:
- `llma-evaluation-list` — eval **config** only (name, type, enabled). Pass-rates are NOT
here — read the trend from `$ai_evaluation` events via `execute-sql` (the reliable path).
- `llma-evaluation-summary-create` — optional AI pass/fail/N/A pattern summary (billed,
rate-limited, currently prone to 500s — a drill-down, not the spine). Pair with
`llma-evaluation-get` / `-test-hog`.
- `llma-tagger-list` / `llma-score-definition-list` — the enrichment config surface
(auto-taggers and scorers — LLM/Hog jobs that can silently break).
- `llma-clustering-job-list` / `-get` — semantic clusters over traces/generations.
- `llma-prompt-list` / `-get` — prompt versions, for correlating a change to its cause.
Schema:
- `read-data-schema` — discover events, properties, and the team's custom dimensions
before filtering or grouping on them.
Harness-level:
- `signals-scout-project-profile-get` — cold orientation snapshot.
- `signals-scout-scratchpad-search` / `signals-scout-scratchpad-remember` — durable steering across runs.
- `signals-scout-runs-list` / `signals-scout-runs-retrieve` — what prior runs found.
- `signals-scout-emit-signal` — emit a finding.
Deep-dive skills (baked into the sandbox — read the matching one when you go deep, don't
reinvent its queries): `posthog:exploring-llm-costs`, `posthog:exploring-llm-traces`,
`posthog:exploring-llm-evaluations`, `posthog:exploring-llm-clusters`, and
`posthog:querying-posthog-data`. See `references/lenses.md` for which skill maps to which
lens.
## When to stop
- Scratchpad + recent runs + profile are quiet → close out empty.
- A candidate matches a scratchpad entry with `noise:` / `addressed:` / `dedupe:` key
prefix → skip with a one-line note.
- You've validated some hypotheses and emitted what's solid → close out, even if
there's more you could look at. Fewer, better signals.
"Looked but found nothing meaningful" is a real outcome, not a failure.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!