Agent Skillsnubjs/nub › ci-triage

ci-triage

GitHub

通过查询 GitHub Commit 的 check-runs API,准确诊断 CI 状态及失败原因,避免因 run-level conclusion 汇总丢失导致的漏报。

.claude/skills/ci-triage/SKILL.md nubjs/nub

触发场景

检查分支、提交或 PR 是否通过 有人报告 CI 失败时 发布版本前 确认提交状态为绿色前

安装

npx skills add nubjs/nub --skill ci-triage -g -y
更多选项

非标准路径

npx skills add https://github.com/nubjs/nub/tree/main/.claude/skills/ci-triage -g -y

不安装直接使用

npx skills use nubjs/nub@ci-triage

指定 Agent (Claude Code)

npx skills add nubjs/nub --skill ci-triage -a claude-code -g -y

安装 repo 全部 skill

npx skills add nubjs/nub --all -g -y

预览 repo 内 skill

npx skills add nubjs/nub --list

SKILL.md

Frontmatter
{
    "name": "ci-triage",
    "description": "Find and diagnose RED CI with the gh CLI — answer \"is anything failing?\" and \"why?\" correctly, without being handed a URL. Invoke (via the Skill tool) whenever you need to check whether a branch, commit, or PR is green, whenever someone reports failing CI, before cutting a release, and before claiming any commit is green. THE FAILURE THIS SKILL EXISTS TO PREVENT: `gh run list` reports a RUN-level `conclusion`, and a run whose rollup is `cancelled` can contain a job whose conclusion is `failure` — so filtering runs on `conclusion==\"failure\"` silently misses real red, and taking the latest run per workflow hides an older failed run on the same SHA. The authoritative instrument is the commit's CHECK-RUNS, which is what the GitHub UI renders. Pairs with `ci-watch` (blocking until a run is terminal) and `ci-adhoc-test` (running a probe on a real OS)."
}

Investigating CI

ci-watch answers "has it finished, and did it pass?" for one run you are waiting on. This skill answers the two questions you get asked cold: "is anything red?" and "why is this red?" — starting from a branch name or a commit, never from a URL someone hands you.

The rule

Never answer "is it green?" from run-level conclusion. Query the commit's check-runs.

A GitHub Actions run rolls up its jobs into one conclusion, and that rollup is lossy in the exact case that matters:

What happened Run conclusion Job conclusions gh run list shows
A job failed failure one failure ✅ red — you see it
A job failed inside a run that also got cancelled cancelled one failure nothing — filtered out
Superseded by cancel-in-progress cancelled all cancelled ❌ nothing (correctly)

The middle row is real and it is what the UI paints red on the commit. It happens whenever a workflow has an aggregation/gate job that runs after its matrix legs: concurrency: cancel-in-progress cancels the legs, the gate still runs, sees cancelled, and fails. The run rolls up cancelled; the gate job is failure.

The one command that is always correct

SHA=$(git rev-parse origin/main)   # or a PR head, or any commit
gh api repos/nubjs/nub/commits/$SHA/check-runs --paginate \
  --jq '.check_runs[] | select(.conclusion != "success" and .conclusion != "skipped" and .conclusion != "neutral")
        | "\(.conclusion // .status) | \(.name) | \(.html_url)"'

Empty output means genuinely green. Anything else is exactly what a human sees in the Actions tab, with a clickable URL per finding. This endpoint is the source of truth because it is what the UI renders — run-level data is downstream of it.

Counting, and the --paginate trap

--paginate runs --jq once PER PAGE. A per-item filter like the one above is fine — each page emits its own lines and they concatenate. But any aggregation (length, add, group_by) silently returns one result per page instead of a total:

# WRONG — prints "0" then "1" on a 2-page response. Reads like 0 if you only look at line 1.
gh api repos/nubjs/nub/commits/$SHA/check-runs --paginate --jq '[.check_runs[]|select(.conclusion=="failure")]|length'

Use --slurp to get one array across pages, and pipe to external jq--slurp is rejected together with --jq:

# RIGHT — one number for the whole commit.
gh api repos/nubjs/nub/commits/$SHA/check-runs --paginate --slurp \
  | jq '[.[].check_runs[] | select(.conclusion=="failure")] | length'

This is not hypothetical: a release commit here carried 192 check-runs across 2 pages, and the naive count printed 0 on the first line.

Diagnosing one failure

Given a failing check, get the run, then the job, then the log — in that order.

gh run view <run-id> --json name,status,conclusion,headBranch,headSha,event,createdAt
gh run view <run-id> --json jobs --jq '.jobs[] | "\(.conclusion // .status) | \(.name)"'
gh run view <run-id> --log-failed > /tmp/fail.log 2>&1; echo "EXIT=$?"
sed -e 's/\x1b\[[0-9;]*m//g' /tmp/fail.log | grep -viE '^\s*$' | tail -40

Strip ANSI escapes (sed -e 's/\x1b\[[0-9;]*m//g') or the log is unreadable. --log-failed returns only failing steps, which is usually 40 lines rather than 40,000.

Read the event and createdAt fields before concluding anything. They tell you whether the run was superseded, and by what.

Superseded-run triage — is this red REAL?

A red check on a commit is not automatically a broken commit. Decide with this:

  1. List every run of that workflow on that SHA. More than one is the tell.
    gh run list --workflow <file>.yml --limit 30 \
      --json databaseId,status,conclusion,headSha,headBranch,event,createdAt \
      --jq '.[] | select(.headSha=="'"$SHA"'") | "id=\(.databaseId) | \(.conclusion // .status) | \(.event) | \(.createdAt)"'
    
  2. If a later run of the same workflow on the same SHA is fully green, the code is fine — the red one was superseded. The commit still shows red, which is a workflow defect worth fixing, not a code defect.
  3. Check the concurrency group. group: ${{ github.workflow }}-${{ github.ref }} means a schedule run and a push run on main share a group and will cancel each other, because both carry refs/heads/main. That is a common and surprising source of self-inflicted red.

The gate-job anti-pattern, and its fix

A gate job that aggregates matrix legs must treat cancelled as not a verdict. This is wrong:

[[ "$smoke" == "success" || "$smoke" == "skipped" ]] || { echo "smoke failed"; exit 1; }

A superseded run sets smoke=cancelled, so the gate fails and paints the commit red forever. Accept cancelled too — it does not mask a real failure, because a genuinely failing leg aggregates to failure, not cancelled:

[[ "$smoke" == "success" || "$smoke" == "skipped" || "$smoke" == "cancelled" ]] || { echo "smoke failed"; exit 1; }

What NOT to do

  • Do not filter gh run list on conclusion=="failure" and call the absence of hits "green". That is the whole bug this skill exists for.
  • Do not take the latest run per workflow (group_by(.name) | max_by(.createdAt)). A later scheduled run can hide an earlier failed push run on the same SHA.
  • Do not trust --branch main alone to find everything; a check can be attached by an app or a workflow_run. Check-runs on the SHA catch all of them.
  • Do not conclude "not failing" from a bot issue being absent. The trunk-red bot files on its own schedule and its silence proves nothing.
  • Do not read an exit code through a pipe (gh ... | head then $?). Redirect, capture $? on its own line, then inspect.

Reporting a finding

State the commit, the check name, the run URL, whether a later run on the same SHA passed, and the mechanism. "CI is red" without those is not a diagnosis. If a superseded run is the cause, say so plainly and name the fix — the commit is still red in the UI and someone has to look at it.

版本历史

  • 0695080 当前 2026-08-06 12:45

同 Skill 集合

.agents/skills/linux-vm-test/SKILL.md
.agents/skills/windows-vm-test/SKILL.md
.claude/skills/audit-thread/SKILL.md
.claude/skills/download-stats/SKILL.md
.claude/skills/git-archaeology/SKILL.md
.claude/skills/linux-vm-test/SKILL.md
.claude/skills/md-toc/SKILL.md
.claude/skills/plan-thread/SKILL.md
.claude/skills/pm-perf-tracing/SKILL.md
.claude/skills/soak/SKILL.md
.claude/skills/todo/SKILL.md
.claude/skills/windows-vm-test/SKILL.md
skills/nub/SKILL.md
.agents/skills/agent-browser/SKILL.md
.claude/skills/ad-hoc-test/SKILL.md
.claude/skills/address-issue/SKILL.md
.claude/skills/agent-browser/SKILL.md
.claude/skills/aube-bump/SKILL.md
.claude/skills/aube-sync/SKILL.md
.claude/skills/ci-adhoc-test/SKILL.md
.claude/skills/ci-watch/SKILL.md
.claude/skills/cpu-reduction/SKILL.md
.claude/skills/dev-loop/SKILL.md
.claude/skills/epic/SKILL.md
.claude/skills/impact-analysis/SKILL.md
.claude/skills/implementation-thread/SKILL.md
.claude/skills/probe-platforms/SKILL.md
.claude/skills/prose-writing/SKILL.md
.claude/skills/release/SKILL.md
.claude/skills/remote-build/SKILL.md
.claude/skills/rust-build-hygiene/SKILL.md
.claude/skills/rust-build/SKILL.md
.claude/skills/visual-review/SKILL.md
.claude/skills/worktree/SKILL.md

元信息

文件数
0
版本
0695080
Hash
59143641
收录时间
2026-08-06 12:45

首页 - Wiki
Copyright © 2011-2026 iteam. Current version is 2.155.2. UTC+08:00, 2026-08-06 19:16
浙ICP备14020137号-1 $访客地图$