diagnose-ci Skill
Fetch CI logs for a failing branch, classify the failure type, and write a structured
diagnosis report to {{AUTOSKILLIT_TEMP}}/diagnose-ci/. Called by the orchestrator on ci_watch failure
before routing to resolve-failures.
Invocation
/autoskillit:diagnose-ci {branch} [run_id] [ci_failed_jobs] [workflow] [event]
Positional args:
branch— the git branch whose CI run to investigaterun_id(optional) — specific workflow run ID; if absent, discover fromgh run listci_failed_jobs(optional) — JSON array of failed job names fromwait_for_ci, used to scope log fetchingworkflow(optional) — workflow filename (e.g.tests.yml); if provided, scopesgh run listto that workflow only; use-to skipevent(optional) — GitHub Actions trigger event (e.g.push,pull_request); if provided, scopesgh run listto that event only; use-to skip
Critical Constraints
NEVER:
- Modify any source code files
- Run the test suite
- Write files outside
{{AUTOSKILLIT_TEMP}}/diagnose-ci/ - Block on missing
ghCLI — write a minimalfailure_type=unknowndiagnosis instead
ALWAYS:
- Write the diagnosis file before emitting output tokens
- Emit the four output tokens (
diagnosis_path,failure_type,failure_subtype,is_fixable) at the end of the response on their own lines
Context Limit Behavior
When context is exhausted mid-execution, the diagnosis file may be partially written
or absent. The recipe routes to on_context_limit: resolve_ci, which proceeds
best-effort with whatever diagnosis was written (or none).
Before emitting structured output tokens:
- If the diagnosis file was not fully written, emit
diagnosis_path =(empty) - Emit
failure_type = unknown,failure_subtype = unknown,is_fixable = falseas fallback values when analysis was interrupted
Workflow
Step 1: Discover Run ID (if not provided)
If run_id is not provided as an argument (or is -), construct the gh run list command
with any provided filters:
# Base command (always used):
gh run list --branch {branch} --limit 1 --json databaseId,status,conclusion
# If workflow is provided and is not `-`, add:
--workflow {workflow}
# If event is provided and is not `-`, add:
--event {event}
Example with both filters:
gh run list --branch {branch} --workflow {workflow} --event {event} --limit 1 --json databaseId,status,conclusion
Parse the JSON to extract databaseId as run_id.
When gh is not accessible or the command fails, proceed to Step 5 (write diagnosis report).
Step 2: Fetch Failure Summary
gh run view {run_id} --log-failed
Capture the output (stdout). This is the primary failure log.
Step 3: Fetch Per-Job Logs
For each failing job in ci_failed_jobs (or all failed jobs from gh run view if not provided):
gh api repos/{owner}/{repo}/actions/runs/{run_id}/jobs
For each failed job, fetch last 200 lines of logs via:
gh api repos/{owner}/{repo}/actions/jobs/{job_id}/logs
Use gh repo view --json nameWithOwner to resolve {owner}/{repo} if needed.
Step 4: Classify Failure
Analyze the log output to classify failure_type as one of:
test— pytest/jest/unit test failureslint— ruff, flake8, eslint, or formatting failuresbuild— compilation or build errorstype_check— mypy, pyright, or TypeScript type errorsenv— missing environment variables, secrets, or infrastructure issuesunknown— cannot determine from logs
Step 4a: Subtype Classification
After determining failure_type, classify failure_subtype using the following error-pattern decision tree (first match wins):
| Pattern match in log output | failure_subtype |
|---|---|
TimeoutError, deadline exceeded, flake, intermittent |
timing_race |
pytest.*FLAKY, rerun, three-or-more identical test runs |
flaky |
ImportError, ModuleNotFoundError |
import |
fixture, pytest.fixture, collection error |
fixture |
environ, missing ENV_VAR, KeyError.*environ |
env |
| Assertion error with stable stack trace (same file/line across runs) | deterministic |
| No pattern matched | unknown |
Guidance for resolve-failures: Include a supplementary "Suggested Starting Verdict" field in the
Recommended Fix Approach section that maps the subtype to an initial verdict for resolve-failures to
consider (not authoritative — resolve-failures makes its own verdict decision from the subtype +
local test result):
failure_subtype |
Suggested starting verdict |
|---|---|
flaky or timing_race |
flake_suspected |
deterministic |
ci_only_failure (if local tests pass) or real_fix (if fixable locally) |
fixture or import |
flake_suspected (if local tests pass) |
env |
flake_suspected |
unknown |
flake_suspected |
Determine is_fixable:
truefortest,lint,build,type_checkfalseforenv,unknown
Step 5: Write Diagnosis Report
Create directory {{AUTOSKILLIT_TEMP}}/diagnose-ci/ if it doesn't exist. Write the diagnosis file:
# CI Diagnosis: {branch}
**Run ID:** {run_id}
**Failure Type:** {failure_type}
**Failure Subtype:** {failure_subtype}
**Is Fixable:** {is_fixable}
**Branch:** {branch}
## Structured Output (machine-readable)
failure_subtype = {failure_subtype}
## Log Excerpt
{first 200 lines of failure log}
## Recommended Fix Approach
{1-3 sentences describing how resolve-failures should approach this}
**Suggested Starting Verdict:** {suggested starting verdict from the subtype table above}
Save to {{AUTOSKILLIT_TEMP}}/diagnose-ci/diagnosis_{timestamp}.md. (relative to the current working directory)
Step 6: Emit Output Tokens
Emit these tokens on their own lines at the end of your response:
IMPORTANT: Emit the structured output tokens as literal plain text with no markdown formatting on the token names. Do not wrap token names in
**bold**,*italic*, or any other markdown. The adjudicator performs a regex match on the exact token name — decorators cause match failure.
diagnosis_path = /absolute/path/to/{{AUTOSKILLIT_TEMP}}/diagnose-ci/diagnosis_{timestamp}.md
failure_type = test|lint|build|type_check|env|unknown
failure_subtype = flaky|timing_race|deterministic|fixture|import|env|unknown
is_fixable = true|false
gh Unavailable Fallback
When gh is not accessible at any step, write a minimal diagnosis:
failure_type = unknownfailure_subtype = unknownis_fixable = false- Diagnosis body: "gh CLI unavailable — logs could not be fetched. Manual inspection required."
Then emit the output tokens and exit.