/investigating-ci-failures
Investigates a specific CI failure to a verdict: whose fault, which commit, who wrote it, and whether it's fixed. Use for "who broke master", "why did this test fail in CI", "is this failure my PR's fault or everyone's", "is this test flaky or actually broken", "when did this
$ npx -y skills add posthog/posthog --skill investigating-ci-failures --agent claude-codeHow 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
/investigating-ci-failures
Context preview
The summary Claude sees to decide when to auto-load this skill.
Investigates a specific CI failure to a verdict: whose fault, which commit, who wrote it, and whether it's fixed. Use for "who broke master", "why did this test fail in CI", "is this failure my PR's fault or everyone's", "is this test flaky or actually broken", "when did this
SKILL.md
investigating-ci-failures.SKILL.mdname: investigating-ci-failures
description: >
Investigates a specific CI failure to a verdict: whose fault, which commit, who wrote it, and
whether it's fixed. Use for "who broke master", "why did this test fail in CI", "is this failure
my PR's fault or everyone's", "is this test flaky or actually broken", "when did this failure
start". Works from the engineering_analytics warehouse views (engineering_analytics_ci_failures,
engineering_analytics_ci_job_history) plus the CI failure logs. Not for aggregate CI health, cost,
or merge bottlenecks (use diagnosing-ci-and-merge-bottlenecks) and not for building saved insights
(use turning-engineering-analytics-into-insights).
Investigating CI failures
The job: take one failing test or one red run and get to a verdict a developer can act on — _yours / trunk-borne / flaky_, and when trunk-borne: the culprit SHA, its author, the PR, and whether a fix already landed. Everything below is derivation over data that already exists; you never need to re-run CI to answer.
Two warehouse views are the substrate (both non-materialized — always current, query them freely):
- **`engineering_analytics_ci_failures`** — one row per pytest `FAILED <nodeid>` line from CI logs,
pre-fingerprinted (`fingerprint` = test id + digit/hex-normalized error). Group by `fingerprint` to get first/last seen, occurrence count, and branch spread.
- **`engineering_analytics_ci_job_history`** — one row per job attempt with `conclusion` AND commit
attribution: `head_sha`, `commit_author_name`, `commit_message`, `commit_pr_number` (the merged PR that produced the commit, the only PR attribution a master push run has). This is where greens live; the logs are failure-only, so every "when did it turn red / green again" question must come from here, never from the logs.
Copy-ready SQL for every step is in [references/investigation-queries.md](./references/investigation-queries.md).
Start wide: what's broken right now
For "what CI failures should I care about right now" (before you have a specific test in hand), the `engineering-analytics-broken-tests` MCP tool does the shape classification below across _all_ live failures at once: it groups the last 2 days of failures by fingerprint and labels each `breaking_master` / `blocking_merge_queue` / `novel_burst` / `potentially_resolved` / `flaky` / `pr_only`, most urgent first, plus `breaking_master_jobs` (default-branch jobs whose latest run is red). Use it as the triage entry point, then drop into the per-failure workflow below to reach a culprit. It is the automated counterpart to fingerprinting by hand; the manual queries stay the way to pin a specific failure to a boundary and author.
`blocking_merge_queue` is the one shape the manual table below does not cover, because it looks like a single-branch failure and is not. The merge queue runs the full suite on a gate branch (`trunk-merge/pr-<n>/…`) carrying the PR rebased onto trunk, so a failure there is on a commit that already passed the PR's own CI: a conflict with what landed in between, not that PR's own bug. Read it as "this stopped a merge", and diff the PR against trunk rather than reading the PR alone.
The four failure shapes
Fingerprint the failure first (query 1 in the references), then read its shape — the classification falls out of three columns:
| Shape | Reading | Next step | | --------------------------------------- | ------------------------------- | ---------------------------------------------------- | | 1 branch, any window | That PR's own problem | Read its failure lines; done | | 1 `trunk-merge/pr-<n>/…` gate branch | Conflict with what landed since | Diff the PR against trunk, not the PR alone | | Many branches, dense burst, hits master | Trunk break (master is/was red) | Boundary query → culprit (below) | | Many branches, sporadic over days/weeks | Flaky | Corroborate with `engineering-analytics-flaky-tests` |
Why cross-branch means trunk: PR CI runs the PR **merged with master**, so one bad master commit fails every concurrently-running PR. A failure appearing on many unrelated branches in a tight window is the signature of a master-merge break, not of those PRs' code. Tell the asker explicitly when their PR is not at fault — that is usually the single most valuable sentence in the answer.
Trunk break → culprit
Run the boundary query (query 2): master-only job history for the failing job, ordered by `created_at`. The pattern reads directly:
... success success | failure failure ... failure | success ...
^ first red = the culprit row ^ first green = the fix rowThe culprit row carries everything: `head_sha`, `commit_author_name`, `commit_message` (which names what changed), `commit_pr_number`. The first-green row identifies the fix the same way. Confidence check before naming anyone: does the culprit commit plausibly touch the failing area (its message / PR diff vs the failing test's module)? A boundary landing on an unrelated commit means sharding or timing noise — widen the window and check the adjacent commit before asserting.
Then verify the failure window in `ci_failures` matches (first_seen just after the culprit merged, last_seen shortly after the fix as the PR queue drained). Mismatch = you're looking at two different problems sharing a test.
Flaky → corroborate, don't guess
Sporadic shape alone is suggestive, not proof. The `engineering-analytics-flaky-tests` MCP tool reads per-test CI spans (rerun-pass signal — a test that failed then passed on retry in the same job) and is the stronger signal where it has coverage. Counts only, never rates: passing runs below the emitter's duration threshold aren't recorded, so there is no honest denominator.
C
Read more
name: investigating-ci-failures description: > Investigates a specific CI failure to a verdict: whose fault, which commit, who wrote it, and whether it's fixed. Use for "who broke master", "why did this test fail in CI", "is this failure my PR's fault or everyone's", "is this test flaky or actually broken", "when did this failure start". Works from the engineering_analytics warehouse views (engineering_analytics_ci_failures, engineering_analytics_ci_job_history) plus the CI failure logs. Not for aggregate CI health, cost, or merge bottlenecks (use diagnosing-ci-and-merge-bottlenecks) and not for building saved insights (use turning-engineering-analytics-into-insights).
Investigating CI failures
The job: take one failing test or one red run and get to a verdict a developer can act on — _yours / trunk-borne / flaky_, and when trunk-borne: the culprit SHA, its author, the PR, and whether a fix already landed. Everything below is derivation over data that already exists; you never need to re-run CI to answer.
Two warehouse views are the substrate (both non-materialized — always current, query them freely):
- **`engineering_analytics_ci_failures`** — one row per pytest `FAILED <nodeid>` line from CI logs,
pre-fingerprinted (`fingerprint` = test id + digit/hex-normalized error). Group by `fingerprint` to get first/last seen, occurrence count, and branch spread.
- **`engineering_analytics_ci_job_history`** — one row per job attempt with `conclusion` AND commit
attribution: `head_sha`, `commit_author_name`, `commit_message`, `commit_pr_number` (the merged PR that produced the commit, the only PR attribution a master push run has). This is where greens live; the logs are failure-only, so every "when did it turn red / green again" question must come from here, never from the logs.
Copy-ready SQL for every step is in [references/investigation-queries.md](./references/investigation-queries.md).
Start wide: what's broken right now
For "what CI failures should I care about right now" (before you have a specific test in hand), the `engineering-analytics-broken-tests` MCP tool does the shape classification below across _all_ live failures at once: it groups the last 2 days of failures by fingerprint and labels each `breaking_master` / `blocking_merge_queue` / `novel_burst` / `potentially_resolved` / `flaky` / `pr_only`, most urgent first, plus `breaking_master_jobs` (default-branch jobs whose latest run is red). Use it as the triage entry point, then drop into the per-failure workflow below to reach a culprit. It is the automated counterpart to fingerprinting by hand; the manual queries stay the way to pin a specific failure to a boundary and author.
`blocking_merge_queue` is the one shape the manual table below does not cover, because it looks like a single-branch failure and is not. The merge queue runs the full suite on a gate branch (`trunk-merge/pr-<n>/…`) carrying the PR rebased onto trunk, so a failure there is on a commit that already passed the PR's own CI: a conflict with what landed in between, not that PR's own bug. Read it as "this stopped a merge", and diff the PR against trunk rather than reading the PR alone.
The four failure shapes
Fingerprint the failure first (query 1 in the references), then read its shape — the classification falls out of three columns:
| Shape | Reading | Next step | | --------------------------------------- | ------------------------------- | ---------------------------------------------------- | | 1 branch, any window | That PR's own problem | Read its failure lines; done | | 1 `trunk-merge/pr-<n>/…` gate branch | Conflict with what landed since | Diff the PR against trunk, not the PR alone | | Many branches, dense burst, hits master | Trunk break (master is/was red) | Boundary query → culprit (below) | | Many branches, sporadic over days/weeks | Flaky | Corroborate with `engineering-analytics-flaky-tests` |
Why cross-branch means trunk: PR CI runs the PR **merged with master**, so one bad master commit fails every concurrently-running PR. A failure appearing on many unrelated branches in a tight window is the signature of a master-merge break, not of those PRs' code. Tell the asker explicitly when their PR is not at fault — that is usually the single most valuable sentence in the answer.
Trunk break → culprit
Run the boundary query (query 2): master-only job history for the failing job, ordered by `created_at`. The pattern reads directly:
... success success | failure failure ... failure | success ...
^ first red = the culprit row ^ first green = the fix rowThe culprit row carries everything: `head_sha`, `commit_author_name`, `commit_message` (which names what changed), `commit_pr_number`. The first-green row identifies the fix the same way. Confidence check before naming anyone: does the culprit commit plausibly touch the failing area (its message / PR diff vs the failing test's module)? A boundary landing on an unrelated commit means sharding or timing noise — widen the window and check the adjacent commit before asserting.
Then verify the failure window in `ci_failures` matches (first_seen just after the culprit merged, last_seen shortly after the fix as the PR queue drained). Mismatch = you're looking at two different problems sharing a test.
Flaky → corroborate, don't guess
Sporadic shape alone is suggestive, not proof. The `engineering-analytics-flaky-tests` MCP tool reads per-test CI spans (rerun-pass signal — a test that failed then passed on retry in the same job) and is the stronger signal where it has coverage. Counts only, never rates: passing runs below the emitter's duration threshold aren't recorded, so there is no honest denominator.
C
:hedgehog: PostHog is the leading platform for building self-driving products. Our developer tools – AI observability, analytics, session replay, flags, experiments, error tracking, logs, and more – capture all the context agents need to diagnose problems, uncover opportunities, and ship fixes. Steer it all from Slack, web, desktop, or the MCP.
Repo: posthog/posthog
Other skills on posthog.
- /analyzing-expensive-users
Analyze the most expensive users in AI observability and explain why they cost so much. Use when the user asks about top spenders, expensive users, per-user LLM cost, user-level cost drivers, or patterns behind high AI observability spend.
Open skill - /creating-online-evaluations
Author continuously-running online evaluations in PostHog AI observability, grounded in real failure modes you've identified. Use when the user wants evaluations that automatically score new generations or whole traces going forward — "create an eval to catch X", "continuously
Open skill - /exploring-ai-failures
Find where an AI/LLM application is failing in production and surface the failure patterns, working from real traces. Use when someone wants to understand what's going wrong with an AI feature, find and categorize failure modes, triage errors, or investigate quality issues
Open skill - /exploring-llm-clusters
Investigate AI observability clusters — understand usage patterns in AI/LLM traffic, compare cluster behavior, compute cost/latency metrics, and drill into individual traces within clusters.
Open skill - /exploring-llm-costs
Investigate LLM spend in PostHog — total cost over time, cost by model, provider, user, trace, or custom dimension, token and cache-hit economics, and cost regressions. Use when the user asks "how much are we spending on LLMs?", "which model / user / feature is most expensive?",
Open skill - /exploring-llm-evaluations
Investigate AI observability evaluations — `hog` (deterministic code-based), `llm_judge` (LLM-prompt-based), and `sentiment` (user-message sentiment). Find existing evaluations, inspect their configuration, run them against specific generations, query individual results, and
Open skill

