triage

SkillAI & models

This skill classifies GitHub issues that do not yet have the triaged marker, so a backlog of unsorted issues gets labeled suggestions without manual review. It runs the classification as a GPT batch job, validates the suggestions against your label taxonomy, and returns a proposed-labels report plus an activity digest. It is read-only, so applying any labels happens afterward in your conversation.

Available today. Use it from your connected AI after setup.

After adding it, ask your agent to classify the untriaged issues in your repository and review the proposed-labels report and activity digest it returns. Then apply whichever labels you agree with in the conversation.

Then ask your AI: use the triage skill

What your AI can do with it

  • Classify untriaged GitHub issues that are missing the triaged marker
  • Run the classification as a single GPT batch job
  • Validate proposed labels against the label taxonomy
  • Receive a proposed-labels report to review
  • Receive an activity digest of the classification run
  • Optionally backfill classification for closed issues

What this skill tells your AI

The instructions your AI receives, as published by clice-io/clice in .claude/skills/triage/SKILL.md and read by ahel’s review.

Triage one batch of untriaged issues. Untriaged = issue without the permanent triaged marker, which apply stamps on every processed issue (title patterns and kind labels are not trusted as processed-state — reporters can produce both themselves). The templates additionally apply needs-triage as a human-visible awaiting signal; apply removes it. The state lives in the labels themselves, so there is no bookkeeping file. Snapshot covers open issues by default; --state all sweeps closed ones too (paired with apply --include-closed) — the full-history migration ran 2026-08, so this is only needed if backfill gaps ever surface again.

1. Snapshot

python3 .claude/skills/triage/scripts/snapshot.py

Fetches every untriaged issue (body + comments) into /tmp/clice-triage/issues/chunk-N/ (25 per chunk), plus digest.json (new issues in the last 7 days, needs-info/needs-repro threads with no activity for over 14 days), existing-labels.json, titles.json, and states.json (snapshot titles and states, used by apply to detect drift). Spaces gh calls with sleep 1 — API rate limits are a real concern. With zero untriaged issues, skip straight to the digest section of the report.

2. Classify

One codex call per chunk; run chunks as parallel background jobs:

codex_root="$(cd "$(dirname "$(command -v codex)")/.." && pwd)"
systemd-run --user --pipe --wait --collect --same-dir \
  --setenv=PATH="$codex_root/bin:/usr/bin:/bin" \
  -p ProtectHome=tmpfs \
  -p BindReadOnlyPaths="$PWD" \
  -p BindReadOnlyPaths="$codex_root" \
  -p BindPaths="$HOME/.codex" \
  codex exec -m gpt-6-astra -c model_reasoning_effort=xhigh \
  --sandbox read-only \
  -o /tmp/clice-triage/verdicts-N.md \
  "Read .claude/skills/triage/rules.md, .github/labels.yml, and every
issue file in /tmp/clice-triage/issues/chunk-N/. Work ONLY from these
local files — no gh, no network. Classify every issue per the rules and
reply with ONLY the JSON array defined by the rules' output schema."

Both sandbox layers are mandatory, never the usual full bypass: issue bodies are untrusted input. Codex's --sandbox read-only blocks writes and command network access but still lets model-run commands read the whole filesystem, so an injected issue could exfiltrate credentials through the verdict text. The systemd-run wrapper closes that: it masks $HOME and rebinds only the repo (read-only), the codex install prefix, and ~/.codex (codex's own state — the one residual exposure).

3. Validate

python3 .claude/skills/triage/scripts/validate.py /tmp/clice-triage/verdicts-*.md

Deterministic gate over the model output: every label must exist in .github/labels.yml, exactly one kind:, no forbidden additions (good first issue, help wanted), no os:wsl mixed with a native os label (existing labels included — such conflicts fail the verdict for manual resolution), every snapshot issue covered, verdicts for unknown issues dropped. Computes add = proposed minus existing labels plus a suggest_remove list (existing labels the model omitted — reported for the maintainer, never auto-removed) and writes /tmp/clice-triage/validated.json. A verdict that fails validation goes to the failures list — report it, never apply it, and do not hand-edit it back in.

4. Report

Return to the main conversation:

  • Proposed changes, one line per issue: #N [conf] +labels — rationale, with title → / ask → sub-lines where the model proposed them.
  • Validation failures and taxonomy gaps.
  • Digest: new issues this week, stale waiting threads (with day counts), open/untriaged totals.

Do NOT apply anything in the forked run — the maintainer reviews the proposals first.

5. Apply (main conversation, after approval)

python3 .claude/skills/triage/scripts/apply.py /tmp/clice-triage/validated.json \
  [--only N,N | --skip N,N] [--retitle N,N] [--include-closed]

Before editing, apply refetches each issue's live labels and title and reconciles the verdict's full label set against them. An issue whose state flipped since the snapshot (closed, or reopened during a closed backfill) or that gained triaged in the meantime is skipped — the verdict was produced for a state that no longer exists. Closed issues are skipped; with --include-closed they are edited instead, and any status: labels — proposed or live — are stripped, since a settled thread has no state. needs-triage is removed. Kind conflicts split on that marker: while it is still present, template kind: labels differing from the model's are replaced; once the marker is gone, the existing kind is a maintainer decision — it wins, and the model's kind, its status: labels, and its derived title rewrite are all dropped. Label edits run removals first and stamp triaged in a separate final call, so a partial failure never hides an inconsistent issue from future snapshots. An issue carrying several maintainer kinds (no marker), or whose live labels would combine os:wsl with a native os label, is skipped for manual resolution. Beyond that, labels are only ever added — removals stay manual via the suggest_remove report. Title rewrites apply to --retitle all or explicitly listed issues, and are skipped when the live title changed after the snapshot. ask_reporter suggestions are never posted automatically — the maintainer sends them personally if worthwhile.

Signals

GitHub stars
1k
Forks
81
Last commit
Sep 2026
Hacker News mentions
20
Advanced
Catalog kind
skill
Gateway key
triage-clice-io
Source
github.com/clice-io/clice