Forty red tests at 9 a.m. is not information — it is noise wearing a dashboard. Clustering failures into product-bug, test-bug, and infra buckets, with an AI drafting the grouping from the raw export, turns the wall of red into a five-minute brief with owners and next actions.
Tier: Core · Workflow area: Triage, flakes & defect intelligence
This playbook builds the morning ritual: export overnight failures, have an assistant cluster them by shared signature into the three buckets, verify the risky clusters by hand, and publish a triage brief the team can act on before standup.
Export, don't screenshot. Pull the structured failure list with messages and traces:
# example: collect junit xml from last night's run artifacts
gh run download --name junit-results -D /tmp/nightly && \
python scripts/failures_to_json.py /tmp/nightly/*.xml > /tmp/failures.json
Include per-test: name, file, failure message, first stack frames, duration, agent/shard, and retry outcome if retries ran.
Prompt for clustering into the three buckets. Signature first, bucket second, evidence always:
Attached: 40 test failures from last night (JSON). Cluster them:
1) Group tests whose failure SIGNATURE is shared — same exception type,
same message shape, same failing endpoint/selector, same shard.
2) Label each cluster PRODUCT-BUG / TEST-BUG / INFRA with reasoning.
Rules: many unrelated features failing identically at the same time
suggests INFRA; failures only in tests touched by yesterday's commits
suggest TEST-BUG; consistent failures matching a feature area with a
recent app deploy suggest PRODUCT-BUG.
3) For each cluster: representative failure quote, member count, member list,
confidence, and the ONE verification step a human should do first.
Output as a table ordered by (product-bug first, then size).
Read the draft skeptically — you are the second clusterer. Expect a good first cut and specific errors: two distinct bugs merged because both say TimeoutError, or an infra cluster hiding one real product failure with a coincidentally similar message. Sample draft row:
CLUSTER C2 · 11 tests · INFRA (confidence: high)
Signature: "ECONNREFUSED payment-sandbox:8443" across checkout, refunds, invoices
Verify first: curl the sandbox healthcheck; check sandbox deploy log 01:00-03:00
Verify the high-stakes clusters by hand. Product-bug clusters gate releases and page developers — confirm one representative failure per cluster yourself (rerun it, read its trace) before the brief asserts it. Infra clusters get the model's proposed one-step check (curl the dependency, check the runner pool). Test-bug clusters get cross-referenced against yesterday's suite commits.
Publish the brief — one screen, owners attached.
NIGHTLY TRIAGE — Tue 05 Aug · run #2031 · 40 failures -> 4 clusters
● PRODUCT (1): C1 order-total rounding, 6 tests, repro'd by hand,
matches deploy 22:14 of pricing-svc -> BUG-3312 filed, owner @pricing
● INFRA (2): C2 payment-sandbox down 11 tests (sandbox restarted, rerun green);
C3 runner-pool OOM 19 tests -> ticket INFRA-88, reruns queued
● TEST-BUG (1): C4 stale fixture after schema migration, 4 tests -> PR #501
Signed: <triage duty engineer>
Every failure is in exactly one cluster; every cluster has a next action and a name.
Feed the loop. File the product bugs (defect-report playbook), send test-bug clusters to fix PRs, recurring infra clusters to platform tickets, and any "flaky" leftovers into the failure-signature diagnosis protocol. Track cluster recurrence week over week — a signature that returns nightly is a systemic issue wearing a daily disguise.
The bucket labels carry consequences — product-bug pages a developer, infra pauses the release question, test-bug lands on QA's own desk. The model proposes the labels; a human verifies every cluster that carries a consequence, and signs the brief.
expected 200 got 500; clustering on message alone merges them. Endpoint, feature area, and timing belong in the signature.Failure exports carry internal URLs, service names, and sometimes customer data embedded in assertion messages — scrub with the same masking pipeline used for test data before sending to external models. On client engagements, prefer client-approved tooling or a local model for the clustering pass; the taxonomy rules in the prompt keep even mid-size local models useful. The brief itself often goes in client-visible channels: keep it evidence-based and tool-agnostic ("clustered and verified" — the client cares about the verification, not the clustering engine).
Keep every morning's brief in one running document. After a month you own a searchable failure-signature history — and that corpus is exactly what makes both the clustering prompt sharper ("we saw this signature on July 12; same cause?") and your quarterly quality report write itself.