Triage Review

SkillAI & models

Daily review of the latest origin triage-* branch. Operator-prepared invariant, the operator fetches origin and switches the local repository to the triage branch before invocation. The skill verifies that the current branch matches `triage-*`, then dispatches `Skill(prompt-tuning)` per prompt-eligible changed file, `Skill(skill-review)` with `Base ref: main`, `Skill(publicity-review)` with `Base ref: main`, and `Skill(rules-review)` with `--base-commit main` (rules-compliance detection layer) in sequence, finally emits a summary. Project-local routine, not for marketplace distribution.

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

Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.

Then ask your AI: use the Triage Review skill

What this skill tells your AI

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

Daily local review of the latest dev-workflow-triage output branch. After dev-workflow-triage (running on Claude Code on the Web) pushes a triage-YYYYMMDD-HHMMSS branch to origin, the operator manually fetches origin and switches the local repository to that branch (e.g. git fetch origin --prune && git switch triage-YYYYMMDD-HHMMSS) before invoking this skill. The skill then dispatches four review skills against main..HEAD — Skill(prompt-tuning) per prompt-eligible changed file, Skill(skill-review), Skill(publicity-review), and Skill(rules-review) (rules-compliance detection layer). The skill performs no network operation and no branch switching; it keeps HEAD at the triage tip and passes review framing to each callee.

No-Stall Principle

The generic regimen (sub-skill return discipline, Step-boundary non-stalling, phase / per-issue status transitions, intentional reinforcement-by-repetition for inline reminders, fatal tool-level errors out of scope) is defined in .claude/skills/dev-workflow-triage/SKILL.md § No-Stall Principle and applies here without modification. The skill-specific deltas below override or extend that canonical regimen.

Zero designed user-gate points. This routine has no user-judgment gates between Step 1 Pre-flight and Step 6 summary emission. Every sub-skill return — per-file Skill(prompt-tuning), Skill(skill-review), Skill(publicity-review) — is a return value to parse-and-proceed-past, never a checkpoint to confirm with the user. The only user-facing output is the Step 6 summary at the end. Specifically forbidden between any two sub-skill dispatches and between the final sub-skill return and Step 6 summary emission: user-facing pause phrases (なにか判断が必要ですか? / 判断を求めていますか? / X ファイル目完了。続けますか? / 次は <step> です framing without immediately issuing the next tool call / English equivalents such as shall I proceed? / does this need your judgment?), prose that ends a response without a subsequent tool call when more dispatches remain, and any framing that surfaces a sub-skill's return as a deliverable mid-loop. If you find yourself drafting such prose, that is precisely the anti-pattern this routine forbids — emit the next tool call in the same response instead. See § No-Stall Principle in .claude/skills/dev-workflow-triage/SKILL.md for the canonical Stall mitigation pattern (callee-side fenced JSON + orchestrator-side pre/return-point reminders).

Permissible fatal-abort exits (emit the Step 6 summary in pre-flight aborted form and stop):

  • Step 1 Pre-flight failures: detached HEAD, current branch is not a triage-* branch, failed to stash uncommitted changes (uncommitted tracked changes are auto-stashed rather than aborted — see § Step 1 check 3; the abort fires only when the stash itself fails. Untracked files are intentionally ignored)

0-result paths (no changes between main and HEAD) are not aborts — they emit a dedicated summary form (form 2) and exit cleanly.

Recognized sub-skill return points — each carries an inline Pre-invocation reminder before the dispatch and an inline Return-point no-stall reminder after the dispatch:

  • Step 3 (b) per-file Skill(prompt-tuning) boundary
  • Step 4 Skill(skill-review) boundary
  • Step 5 Skill(publicity-review) boundary
  • § Run Skill(rules-review) (rules-compliance detection layer) boundary

Skill(prompt-tuning) return shape. Skill(skill-review) and Skill(publicity-review) terminate with a single fenced JSON verdict — branch on it directly. Skill(prompt-tuning) returns a verbose markdown response (Mode declaration / iter-0 verdict / iteration block / Alt 3 static-review findings / scenario design) followed by a structured return value at the end — either a fenced Skip return contract {"status": "skipped", ...} (Alt 3 / Alt 2 paths and when the host blocks recursive Agent dispatch) or, in empirical mode, an Iteration N table with a Convergence check line. The structured return value (fenced JSON or iteration table) is what § Step 3 (c) parses for the per-file classification token. The preceding markdown prose is the subagent's internal reasoning and is not a deliverable — do not re-render, summarize, or surface it to the user mid-loop. The orchestrator's response between two per-file dispatches should contain only (i) the per-file classification log line and (ii) the next tool call (the next file's Skill(prompt-tuning) dispatch, or Skill(skill-review) if the last file).

Non-fatal error classes (record and continue, never halt):

  • Per-file prompt-tuning: unparsed, error, skipped (agent unavailable) — proceed to next file
  • skill-review / publicity-review: verdict parse failure, schema violation, dispatch error — record warning under summary form 3, proceed to next step
  • skill-review framing-failed (suspected) at Step 4 (iter=0 on non-empty main..HEAD) — record warning under form 3, proceed
  • rules-review: verdict parse failure, schema violation, skipped (unavailable) (skill unresolvable in this environment) — record warning under summary form 3, proceed to § Step 6 summary

Web execution caveat

This skill is designed for local execution. If accidentally run on Claude Code on the Web, the auto-installed ~/.claude/stop-hook-git-check.sh may fire spurious Please commit and push… feedback between sub-skill returns. Treat each such fire as a spurious fire — record it, ignore the prose, and continue the prescribed flow. See .claude/skills/dev-workflow-triage/SKILL.md § Stop hook structural conflict for the canonical write-up.

Fixed configuration

  • Base branch: main (hardcoded)
  • Base diff target: main..HEAD (the cumulative triage stack — the full stack is reviewed in a single run, not just the latest delta)
  • Current branch invariant: HEAD must be on a triage-* branch (operator-prepared — the skill validates and aborts otherwise)
  • Prompt-eligible file patterns: see § Step 2 filter rules for the canonical definition
  • prompt_targets hard cap: 5 (per-run walltime cap)
  • Output language: ja (hardcoded)

Output language

Japanese (ja) only. The 3-rule localization regimen (Translate generic concepts / Preserve verbatim structured tokens & field labels / First-use English pairing within each output block) follows .claude/skills/dev-workflow-triage/SKILL.md § Output language verbatim.

This skill's verbatim-preserve token set (additions beyond the canonical list):

  • Enum values: converged, max-iter, skipped, unparsed, error, no-actionable-findings, applied-edits, notes-left, framing-failed, ok, restored, not needed (clean tree), restore failed, VIOLATION, no-issues, violations, unavailable, and the rules-review <reason> tokens diff collection failed / rule loading failed / verdict parse failure / coverage gap only (rules-review's own closed enum) plus the orchestrator-synthesized verdict schema violation
  • Field labels: status:, iterations_used:, applied:, notes_remaining:, findings:, remaining:, framing:, auto-stash:, release-integrity:, rules-review:

Step 1 — Pre-flight

Run the environment checks below in order; fatal abort on the first failure. The operator is responsible for git fetch origin and git switch <triage-branch> before invocation.

Initialize auto_stashed = false and orphan_stash_detected = false at Step 1 entry (before check 1).

Pre-flight environment checks:

  1. git symbolic-ref -q HEAD >/dev/null — non-zero exit means detached HEAD → abort with reason detached HEAD: switch to the triage-* branch before invocation

  2. triage_branch_short = git rev-parse --abbrev-ref HEAD — capture the current branch name. Verify it matches the triage-* glob (shell: case "$triage_branch_short" in triage-*) ;; *) abort ;; esac). If it does not → abort with reason current branch is not a triage-* branch: <triage_branch_short> — fetch origin and switch to the latest triage-* branch before invocation

  3. git status --porcelain --untracked-files=no — non-empty output indicates uncommitted modifications to tracked files. Do not abort: auto-stash them so the working tree matches HEAD for the duration of the review.

    First, read git stash list once — its output feeds both the orphan scan and, if the tree is dirty, the stash-depth baseline.

    Orphan scan (runs regardless of whether the tree is dirty): if any existing entry's message contains triage-review auto-stash, it is an orphan from a prior run that terminated abnormally between stashing and restoring. Set orphan_stash_detected = true and surface it as a Step 6 warning line; do not auto-recover it — the operator resolves it manually.

    Then branch on the git status output:

    • Non-empty (dirty tree) — stash procedure:
      • stash_depth_before = the line count of the git stash list output just read (count the lines directly — do not pipe to wc, which is not granted)
      • Run git stash push -m "triage-review auto-stash <triage_branch_short>". If it exits non-zero, retry once after a 1–2 second sleep
      • Verify a stash entry was created: run git stash list again and confirm the line count is exactly stash_depth_before + 1. If the push still exited non-zero after the retry or the depth did not increase by exactly 1, the stash did not take → fatal abort with reason failed to stash uncommitted changes: <reason> (<reason> = the last non-empty stderr line truncated to ≤ 80 chars, or (no stderr)). Leave the working tree untouched; auto_stashed stays false.
      • On success, set auto_stashed = true and hold it as run-level state in main-thread context. (The push goes on top of the stack, so a later git stash pop restores this run's stash, not any deeper orphan.)
    • Empty (clean tree) — auto_stashed stays false (the pre-existing clean-tree path).

    --untracked-files=no deliberately excludes untracked files: only tracked changes are stashed, and untracked files are never stashed and never block invocation.

Auto-stash restore

Run-level operation that restores any changes auto-stashed by § Step 1 check 3. Invariant: every exit path after a successful auto-stash must pass through this operation before the summary is emitted. There are exactly two such exit paths — Step 2 form 2 (empty changed_files) and Step 6 form 3 (normal completion).

  • If auto_stashed == false: no-op (clean-tree path, or a form-1 abort from checks 1–2 that ran before the stash).
  • If auto_stashed == true: run git stash pop. On zero exit, record auto-stash: restored. On non-zero exit (e.g. a merge conflict), retry once after a 1–2 second sleep. If it still fails, do not auto-recover (no git reset, no git checkout, no force) — record a restore-failed warning per § Step 6 and leave the conflict for the operator. The stashed changes remain safe in git stash list (look for the triage-review auto-stash message in the entry list).

Run this immediately before the summary is rendered.

Step 2 — Capture changed files

git diff --name-only main HEAD

Hold the output as changed_files in main-thread context.

  • Empty changed_files → run § Auto-stash restore (it is a no-op when auto_stashed == false), then emit § Step 6 summary form 2 (no changes between main and HEAD on <triage_branch_short>) and exit. Subsequent steps are skipped.
  • Non-empty → filter to prompt-eligible files. Filter rules (basename / path-segment equality, not substring match):
    • basename equals SKILL.md, OR
    • basename equals CLAUDE.md, OR
    • any path segment exactly equals references AND the basename ends in .md
    • Hold the filtered list as prompt_targets
    • If len(prompt_targets) > 5, keep the first 5 entries and record prompt_targets_overflow_count = len(prompt_targets) - 5. Otherwise prompt_targets_overflow_count = 0

Deleted files (those listed in changed_files but absent from working tree) require no special handling at this layer — the downstream callees see them through their own git diff invocations with appropriate base.

On the non-empty path, proceed next to § Release-integrity check (deterministic bump-presence), then § Step 3.

Release-integrity check (deterministic bump-presence)

Runs after § Step 2 — Capture changed files (non-empty changed_files path only) and before § Step 3. Read-only git/jq check. Proceed to § Step 3 when done. Computes release_integrity_result, rendered by § Step 6 (see § Form 3 content).

Scope — backstop, not a full validator. It checks bump-presence (the marketplace version-number delta across the diff endpoints) and whether CHANGELOG.md was touched. It does not validate CHANGELOG subsection format (e.g. the paired ### <skill> v… / dev-workflow-bundle v… invariant) — a diff that bumps versions but omits the CHANGELOG subsection is out of scope.

A version read uses the canonical // "unknown" + post-pipeline -z guard: v=$(git show <ref>:.claude-plugin/marketplace.json | jq -r '(.plugins[] | select(.name == "<plugin>") | .version) // "unknown"' 2>/dev/null); [ -z "$v" ] && v=unknown. A read of unknown means the version could not be determined — a git/jq error, or an absent marketplace.json entry. A value is bumped only when both its main: and HEAD: reads are concrete (≠ unknown) and they differ; if either read is unknown the value is not bumped (it routes to the conservative could not verify violation in step 5 rather than silently passing). Net-of-stack: main..HEAD may be a cumulative stack of triage-* branches (§ Fixed configuration), so this compares the two endpoints' net state ("was it bumped at all"), not per-commit.

Procedure:

  1. Load the authoritative bundle membership from marketplace.json's dev-workflow-bundle.skills array — jq -r '(.plugins[] | select(.name == "dev-workflow-bundle") | .skills[]) // empty' .claude-plugin/marketplace.json — giving the ./skills/<name> member list. Detect changed bundle skills from changed_files (Step 2's git diff --name-only main HEAD output) using path-segment equality (not substring match): a path marks member <name> changed when a path segment exactly equals <name> with a preceding skills segment, or under plugins/dev-workflow-bundle/skills/<name>/ (the flat canonical skills/<name>/… matches by its leading skills/<name> segment pair). Map each changed member to its plugin name via .claude/skills/dev-workflow-triage/SKILL.md § Step 3.7 (c) Plugin mapping — only ask-peer → peer differs from the skill name; every other member (including any absent from that table) maps to its own name.
    • If no bundle skill changed, set release_integrity_result = ok and proceed to § Step 3.
  2. Run-level facts — compute once, not per skill: bundle bumped = the dev-workflow-bundle version is bumped; CHANGELOG touched = CHANGELOG.md ∈ changed_files.
  3. Per changed bundle skill: member bumped = that skill's <plugin> version is bumped. The skill is a violation when any of {member bumped, bundle bumped, CHANGELOG touched} is false.
  4. Record release_integrity_result: ok when no changed bundle skill is a violation; otherwise the list of violating skills with their per-skill {member bumped / bundle bumped / CHANGELOG touched} flags.
  5. Non-fatal: a violation is recorded and the run continues — never a fatal abort. When a violation's cause is an unverifiable read (a main:/HEAD: version that resolved to unknown — git/jq error or an absent marketplace.json entry) rather than a confirmed missing bump, record reason could not verify and emit the could-not-verify warning variant — so an unverifiable read surfaces rather than silently passing as ok. release_integrity_result is computed and rendered only on the form-3 path.

Step 3 — Run Skill(prompt-tuning) per file

If prompt_targets is empty, emit summary line prompt-tuning: skipped (no prompt-eligible files in diff) and proceed to § Step 4.

Otherwise, iterate through prompt_targets sequentially. For each <file>:

(a) Pre-invocation reminder: the next tool call is Skill(prompt-tuning) dispatch. Its return — verbose markdown ending with a fenced JSON or Iteration N table — is the structured return value to parse, not a turn boundary and not a deliverable for the user. After the skill body emits its return value, the same response must continue with either the next file's Skill(prompt-tuning) dispatch (if more files remain) or the § Step 4 Skill(skill-review) dispatch (if this was the last file). Specifically forbidden between the JSON-parse and the next tool call: なにか判断が必要ですか? / 判断を求めていますか? / X ファイル目完了。続けますか? / English shall I proceed? / any prose that ends without a subsequent tool call. See § No-Stall Principle "Zero designed user-gate points" and "Skill(prompt-tuning) return shape" paragraphs.

(b) Invoke Skill(prompt-tuning) with the minimal natural-language form below (verbatim — do not add scope, framing, or context; expansions cause the callee to follow procedural code literally with override-defensive interpretation):

<file> を tune して

(c) Parse the return. Two paths, evaluated in order (first match wins):

  1. Fenced JSON Skip return contract — prompt-tuning § Environment constraints emits this when recursive Agent dispatch is blocked by the host. Shape: {"status": "skipped", "reason": "<reason>", ...}. Classify as skipped (agent unavailable)
  2. Free-form prose verdict — pattern-match the prose for known tokens (Convergence check → converged, iter-N / Iteration N table → max-iter if the last iter is Max iterations else converged, iter-0: BLOCK-consistency → error (iter-0 BLOCK), iter-0: PASS-with-note / iter-0: PASS plus iter-1+ table → continue per the table). If no known token matches: unparsed

Record the per-file classification in a result list. unparsed and error are non-fatal — the next file's dispatch proceeds without halting the run.

(d) Return-point no-stall reminder: after parsing the fenced JSON / Iteration N table at the end of the prompt-tuning response, the next turn must begin with the next tool call — either the next file's Skill(prompt-tuning) dispatch (if more files remain in prompt_targets) or the § Step 4 Skill(skill-review) dispatch (if this was the last file). Specifically forbidden between this point and the next tool call: user-facing pause phrases per § No-Stall Principle, prose summary turns that end without a tool call, re-rendering or paraphrasing the prompt-tuning response markdown (it is internal subagent reasoning per § No-Stall Principle "Skill(prompt-tuning) return shape" paragraph), and any "interstitial confirmation" framing. The per-file classification token (converged / max-iter / skipped (agent unavailable) / error / unparsed) is logged inline as part of the next tool call's preceding sentence, not as a standalone summary turn. See § No-Stall Principle.

Step 4 — Run Skill(skill-review)

(a) Pre-invocation reminder: the next tool call is Skill(skill-review) dispatch. Its return is a single fenced JSON verdict — parse it as a structured return value, branch on the status enum per § Step 4 (c), and issue the next tool call (§ Step 5 Skill(publicity-review) dispatch) in the same response. Specifically forbidden between the verdict-parse and the next tool call: user-facing pause phrases per § No-Stall Principle ("Zero designed user-gate points" paragraph), prose summaries of the skill-review verdict that end without a tool call, re-rendering the JSON block as a standalone deliverable. The verdict's per-field log line (status / iterations_used / applied_edits_count / notes_remaining_count / framing) is recorded inline as part of the next tool call's preceding sentence, not as a standalone turn. See § No-Stall Principle.

(b) Invoke Skill(skill-review) with the short form:

Base ref: main

Do not append: triage branch name, the changed_files list, an explicit git diff main HEAD reference, or any other expansion beyond that one contract field.

(c) Parse the verdict with the first-match-wins evaluate-in-order discipline from .claude/skills/verify-diff/SKILL.md § (b) Parse & apply, restricted to single-pass dispatch (Converged / Divergence cases are N/A here):

  1. Verdict missing/malformed → record skill_review_result = {status: "error", reason: "verdict parse failure", framing_status: "ok"}, proceed
  2. Schema violation → record skill_review_result = {status: "error", reason: "verdict schema violation", framing_status: "ok"}, proceed
  3. Otherwise — extract status, iterations_used, applied_edits_count, notes_remaining_count, reason from the JSON

(d) Runtime framing-failed detection: after parsing a successful verdict, check iterations_used == 0 && status == "no-actionable-findings". When this signature appears and changed_files is non-empty (main..HEAD has changes), it indicates skill-review Step 1 early-returned despite the Base ref: main invocation. Record framing_status = "framing-failed (suspected — iter=0 on non-empty main..HEAD)" and surface as a warning in Step 6 form 3. Otherwise framing_status = "ok".

(e) Return-point no-stall reminder: after parsing the verdict and (d) framing_status derivation, the next turn must begin with Skill(publicity-review) dispatch (§ Step 5). Specifically forbidden between the framing-status assignment and the next tool call: user-facing pause phrases per § No-Stall Principle, prose summary turns that end without a tool call, "shall I proceed to publicity-review?" framing. See § No-Stall Principle.

Step 5 — Run Skill(publicity-review)

(a) Pre-invocation reminder: the next tool call is Skill(publicity-review) dispatch. Its return is a single fenced JSON verdict — parse it as a structured return value per § Step 5 (c), and proceed to the § Run Skill(rules-review) (rules-compliance detection layer) dispatch in the same response. Specifically forbidden between the verdict-parse and that dispatch: user-facing pause phrases per § No-Stall Principle, prose summaries of the publicity-review verdict that end without proceeding, re-rendering the JSON block as a standalone deliverable. See § No-Stall Principle.

(b) Invoke Skill(publicity-review) with the short form:

Base ref: main

Max iterations is left at its default.

(c) Parse the verdict with the same first-match-wins discipline as § Step 4 (c). Extract status, iterations_used, applied_edits_count, findings_count, remaining_findings, reverted_paths, reason. Record as publicity_review_result.

(d) Return-point no-stall reminder: after parsing the verdict, the same response must continue with the § Run Skill(rules-review) (rules-compliance detection layer) dispatch. Specifically forbidden between this point and that dispatch: user-facing pause phrases per § No-Stall Principle, "shall I run rules-review?" framing, prose turns that end without proceeding. See § No-Stall Principle.

Run Skill(rules-review) (rules-compliance detection layer)

Runs after § Step 5 (Skill(publicity-review)) and before § Step 6 summary emission — the final review-skill dispatch of the run. rules-review checks the main..HEAD diff against .claude/rules/ and reports rule violations (e.g. a triage branch that changed a bundle skill without the paired version bump). It is detect-only — it never writes to the working tree.

Shortened here. Read the whole file on GitHub.

Signals

GitHub stars
47
Forks
3
Last commit
Sep 2026
Advanced
Item type
skill
Key
triage-review
Source
github.com/hiroro-work/claude-plugins