Files
cc-ci-orchestrator/.claude/skills/cc-ci-cleanup/SKILL.md
T
autonomic-bot fae2fbe21b add /cc-ci-cleanup — reconcile, close dead PRs, report what actually blocks the rest
25 open recipe PRs had accumulated, and the list had stopped being readable: CI
sweep artifacts that were never meant to merge sat next to genuine CVE fixes, and
three PRs the operator had been told were outstanding were in fact already merged
upstream (discourse #6 with 140 CVEs, keycloak #6 with 12, n8n #5) — visible only
once the mirrors were reconciled.

The skill: reconcile every mirror from true upstream FIRST (that step alone closed
those three), survey every open PR deterministically, close the ones that cannot
merge or were never meant to, and report prioritised action items — CVE-carrying
first — for the ones that should land. It never merges a recipe PR.

pr-survey.py gathers the facts and decides nothing: behind_main, mergeable,
diff_files, which images the PR adds vs which are already pinned in main, the
newest !testme verdict, branch kind, age.

One correctness detail worth the extra state: a FAILED diff fetch is reported as
unknown, never as an empty diff. gitea #4 reads that way (force-pushed branch)
while being a verified green fix, and 'empty diff' is a close signal — so the
tool says DIFF-UNREADABLE(do not close on this) instead.
2026-08-11 19:17:00 +00:00

5.4 KiB

name, description
name description
cc-ci-cleanup Tidy the fleet's open recipe PRs. Reconciles every mirror from TRUE upstream first (which alone closes PRs upstream already merged), then surveys every open PR deterministically, CLOSES the ones that can no longer be merged or were never meant to be (CI sweep artifacts, obsolete bumps, superseded duplicates) with a reason, and reports prioritised action items for the ones that SHOULD merge — what specifically is blocking each. NEVER merges a recipe PR. Invoke as /cc-ci-cleanup [recipe ...] [--dry-run].

cc-ci-cleanup

Open recipe PRs accumulate and rot. Some were never meant to merge (CI sweep artifacts), some were overtaken (upstream merged the same change, or a newer PR supersedes them), and some genuinely should land but are quietly blocked. Left alone the list becomes noise, and a real CVE fix hides in it.

This skill separates those three, acts on the first two, and hands you a short list for the third.

Boundaries. It CLOSES irrelevant PRs and NEVER MERGES any recipe PR — those change what deploys on other people's infrastructure, so a human merges them (see AGENTS.md). Closing is the only write it performs, always with a comment saying why.

Arguments

  • <recipe> … — limit to these recipes (else every recipe in cc-ci-plan/used-recipes.md).
  • --dry-run — classify and report, close nothing.

Procedure

1. Reconcile every mirror from TRUE upstream — MANDATORY, FIRST

cc-ci-plan/reconcile-upstream.sh --all        # or: reconcile-upstream.sh <recipe>...

Do not skip this and do not reorder it. Every signal in step 2 is measured against the mirror's main; against a stale mirror they are all wrong. This step also does a chunk of the cleanup by itself — it closes any PR whose changes upstream has already merged.

On the first real run (2026-08-11) this alone closed three PRs that looked pending and were already merged upstream: discourse #6 (carrying 140 CVEs), keycloak #6 (12 CVEs), n8n #5. All three had been reported to the operator as outstanding work. mailu #6 went the same way earlier the same day. Reconciling is not hygiene, it is how you avoid recommending work that is already done.

2. Survey every open PR (deterministic — no judgement yet)

python3 cc-ci-plan/pr-survey.py [recipe ...]          # add --json for the raw facts

Per PR it measures: behind_main, ahead, mergeable, diff_files, the images it adds, which of those are already in main, obsolete, the newest !testme verdict + build, branch_kind, and age/idle days. It decides nothing — that is this skill's job.

3. Classify

CLOSE — cannot merge, or was never meant to. Each needs a positive reason, not an absence:

signal why it is closeable
branch_kind: ci-artifact (ci/*) regall/cfold sweeps and !testme probes — harness artifacts, never intended to merge
obsolete: true every image it adds is already pinned in main — it has nothing left to contribute
superseded a newer PR on the same recipe makes the same bump (name both numbers in the comment)
diff_files: 0 genuinely empty diff — nothing to merge

NEVER close on:

  • DIFF-UNREADABLE — the diff could not be fetched, which is NOT an empty diff. gitea #4 reads that way (force-pushed branch) while being a verified, green, needed fix.
  • any field that came back null/unknown.
  • a PR that carries a CVE fix and is the only thing carrying it, even if it looks stale — report it instead. Losing a security fix to tidiness is far worse than a long PR list.
  • --dry-run.

NEEDS WORK — should merge, something blocks it. Give the specific next action:

signal action item
mergeable: false conflicts — rebase the branch on main and re-run !testme
behind_main > 0 out of date — rebase, then re-verify (a green from before main moved proves nothing)
ci: failed diagnose via /ci-test-review; classify recipe-bug vs stale test
ci: never-run run !testme
blocked on the operator say exactly what is needed (a secret, an upstream release, a decision)

READY — green, current, no conflicts. Action item is simply: review and merge.

4. Close the CLOSE set (skip entirely under --dry-run)

Comment first, then close. The comment must say which signal made it closeable and what to do if that is wrong ("reopen if …"), so a wrong call is cheap to undo. Never close silently.

5. Report

Order by what deserves attention, not by recipe name:

  1. CVE-carrying PRs that should merge — most severe first, with the CVE ids.
  2. Other READY PRs (green + current).
  3. NEEDS WORK, each with its one specific action.
  4. CLOSED this run, with the reason for each.
  5. Anything deliberately left alone despite looking stale, and why.

End with a one-line summary: N open → C closed, R ready to merge, W need work.

Guardrails

  • Never merge a recipe PR. Create/verify/close only; the operator merges.
  • Reconcile first, always. Judging a PR against a stale mirror is how you close good work or recommend work that is already done.
  • Close only on a positive signal, never on "looks old". Age alone is not a reason — several 60-day-old PRs here are green and mergeable.
  • Never close a lone CVE fix. Report it, however stale.
  • Every close gets a comment with its reason and a reopen hint.