| name | diagnose-integration-failure |
| description | Diagnose a failing TMDb integration-test run — summarise the failure, rank the likely cause, and suggest a concrete fix |
Diagnose an integration-test failure
The TMDb integration tests hit the live TMDb API. The suite runs on three
triggers — pull_request / push (where it gates the change) and
schedule (the weekly live-API canary). Which run failed flips the most
likely cause, so determine the trigger first (step 0):
- PR / push-triggered → this run is the gate for a code change, so a
regression in the diff is a real candidate — weigh it alongside backend/data
drift. This is the common case when invoked from
/watch-pr or /fix-pr-checks.
- Scheduled → no code changed, so a regression is unlikely; the cause is
almost certainly a backend change, drifted test data, or a transient error.
Wrong suite? If a CI check failed instead (lint, markdown, build, or
unit tests from ci.yml), use /diagnose-ci-failure. That one leads with the
opposite assumption: a CI failure is almost always caused by the change under
review.
Agent Behaviour Contract
Do these by default, without being reminded.
- Observe before you theorise. The log tells you what broke; only a live
call tells you what the API returns today. So a cause that claims the API
changed shape, or that a test's baseline drifted, must be backed by a live
observation —
mcp__tmdb__* (CLAUDE.md's standing instruction).
- Every such cause carries an
observed: line naming the tool called and
the shape that came back. A cause with no observed: line is not
reportable as ranked — demote it and mark it unverified. This is what
makes the rule checkable by the consumer instead of trusting the
diagnostician.
- Only causes 1 and 2 can be observed. A transient/rate-limit (cause 3) and
an in-diff regression (cause 0) cannot be confirmed by a live call — the
endpoint being healthy now says nothing about either. Do not manufacture an
observed: line for them; the requirement does not apply.
- Never publish a secret. An
observed: line records the tool and the
shape, never a URL, command, or header carrying TMDB_API_KEY. TMDb takes
api_key as a query item, so a pasted curl leaks it — and this analysis
is published verbatim into an issue on a public repo. Say
mcp__tmdb__movie_details(550) → runtime: Int, present, not the command.
- Headless runs cannot probe. The scheduled
integration-failure.yml job
mounts no MCP. There, write observed: unavailable (headless) and mark the
cause unverified — do not fall back to curl, which would put the key
in the text (rule 4). Attended runs have the MCP; use it.
Produce a concise markdown analysis with exactly these three sections:
Summary: one or two sentences on what failed — name the failing
suite/test where visible.
Likely cause: the most probable root cause, ranked most-likely first —
ranked by the trigger (step 0). Each cause 1 or 2 carries its observed:
line, or is marked unverified:
- If PR / push-triggered, add as a top candidate:
0. A regression in the changed code — read the diff (
git diff main...HEAD).
A new/renamed request path or query item, a changed model/CodingKeys, or a
decoder tweak can break a live call. This run is the gate, so the diff is a
prime suspect — but still weigh causes 1–3 below, since the live API can drift
independently of your change.
- If scheduled, do NOT lead with "a code bug" (nothing changed); go
straight to 1–3.
- A TMDb backend change — a response field was added, removed, renamed, or
became nullable, breaking a model's
Decodable. Call the endpoint via
mcp__tmdb__* and record what came back (observed:); confirm against the
OpenAPI spec (see step 3) for the documented shape.
- Stale assumed data in an integration test — the test asserts a specific
live value (a title, count, id, date, or ordering) that the API now returns
differently. The code is fine; the test's baseline has drifted. Fetch the
value the test asserts and record it (
observed:) — drift is only a fact
once you have seen today's value.
- A transient API error, rate limiting (HTTP 429), or a timeout — the
Integration Test job has a 30-minute
timeout-minutes, and a cancelled
(timed-out) run still surfaces as a workflow failure. A truncated log with
no assertion failure, or a run that ran the full 30 minutes, points here.
Cite the specific HTTP status codes, error messages, or failing assertions from
the log that point to your conclusion.
Suggested fix: the concrete next step — which Swift model or JSON fixture to
update for an API change, which integration-test assertion to relax or refresh
for drifted data, or (only for case 3) re-run the job / wait out rate limiting /
investigate the slow run if it timed out.
Keep it under ~150 words.
Steps
-
Determine the trigger (it sets the cause ranking above). If the caller told
you (e.g. /watch-pr / /fix-pr-checks diagnose a PR run), use that.
Otherwise read it from the run with mcp__github__actions_get method
get_workflow_run (owner/repo from the origin remote, resource_id: <id>) →
event is pull_request, push, or schedule (headless / no MCP:
gh run view <id> --json event,displayTitle). If you can't tell, assume
PR/push when a local diff vs main exists, else scheduled.
-
Locate the failure log, in this order — use the first that exists:
- A path the caller handed you (e.g.
failure-log.txt in the working
directory, used by the CI alert workflow).
.build/last-integration-test.log — written by the /integration-test
skill. If you (or the user) haven't run the tests yet locally, run
/integration-test first, then read this log.
- Otherwise, find the failing run with the GitHub MCP (owner/repo from
origin): mcp__github__actions_list method list_workflow_runs
(resource_id: integration.yml, workflow_runs_filter: { status: completed },
then filter to conclusion == "failure"), and read its log with
mcp__github__get_job_logs (run_id: <id>, failed_only: true,
return_content: true). Headless / no MCP:
gh run list --workflow Integration --status failure --limit 1 then
gh run view <id> --log-failed.
-
Read the failing portion — focus on the assertion failures and error
messages, which surface near the end. Read the specific test, model, and
fixture files the log points to so your suggested fix names real symbols.
-
Check the OpenAPI spec when the failure looks like a decode/shape error.
The live spec is the source of truth for the current response shape, but it
is ~3 MB of minified JSON on a single line — NEVER grep, , or
it whole; that dumps the entire file into context. Extract only the
one endpoint you need with (requires , which is present in CI and
installable via Homebrew locally; if is unavailable, skip this step and
say so):