triage
SkillAI & modelsThis 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.
No other account needed.
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-5.6-sol -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, withtitle →/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