Skip to content
Back to skills

Reaper

ASecurity

Interactive, review-first smart-reaper triage from inside a session. Drives the Tier-2 content-judgment pass (the SAME /freshness-triage mechanism the scriptable `reaper --judge` subcommand uses) over the current idle candidate set, presents each candidate's keep/close verdict with its cited HEAD evidence for operator review, and closes a bead ONLY on explicit operator confirmation — mutating no bead state until you approve. Honors every reaper exemption (reaper:keep, the MinPriority floor, f...

  • 4 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added October 5, 2026
developmentgobashrailsapi

Works with

  • cli
  • api

Security analysis

A100/100

Scanned October 5, 2026

npx -y skills add kurofune/claudit --skill reaper --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Reaper?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Reaper
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/kurofune-reaper/badge)](https://www.skillsdirectory.com/skills/kurofune-reaper)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
# Generated by djinn-gen-adapters from theme=djinn. DO NOT EDIT — regenerate via djinn-gen-adapters.
# djinn-compat: 1
name: reaper
description: "Interactive, review-first smart-reaper triage from inside a session. Drives the Tier-2 content-judgment pass (the SAME /freshness-triage mechanism the scriptable `reaper --judge` subcommand uses) over the current idle candidate set, presents each candidate's keep/close verdict with its cited HEAD evidence for operator review, and closes a bead ONLY on explicit operator confirmation — mutating no bead state until you approve. Honors every reaper exemption (reaper:keep, the MinPriority floor, follow-up grace, structural type protections) and the djinn-yume invariant (idle-age alone never closes). Triggers on: /reaper, interactive reaper, smart-reaper triage, review dead beads, content-aware reaping in session, triage the idle backlog."
---

<!-- djinn-compat-check: begin -->
**Before anything else:** run `djinn skills check --here` in the repo you are working in. If it exits non-zero, stop: relay its output to the user verbatim — it names the fix — and run no other step of this skill. On exit 0, continue.
<!-- djinn-compat-check: end -->

# /reaper — Interactive Smart-Reaper Triage

## Overview

`/reaper` is the **interactive, review-first** trigger for the smart reaper. It
drives the **Tier-2 content-judgment pass** over the current idle candidate set
from inside a session and presents each candidate's **keep / close**
verdict — with the cited HEAD evidence behind it — for operator review, then
closes a bead **only on your explicit confirmation**.

> **No `defer` from this surface.** `/reaper` only ever produces **keep** or
> **close**: the `/freshness-triage` edge yields a high-confidence dead verdict
> (`already-done`/`obsolete` → **close**) or `fresh` → **keep**. The reaper's
> `defer` disposition (`reaper.Defer`, `internal/djinn/reaper/judgment.go`) is a
> **scriptable-edge-only** state reachable from `reaper --judge`; this
> interactive surface never emits it.

It is the in-session sibling of the **scriptable `reaper --judge` subcommand**
(`cmd/reaper`, the scheduled/headless surface). The two share ONE judgment
mechanism — the existing **`/freshness-triage`** skill — so they can never
diverge in how a bead is judged. They differ only in trigger and disposition:

| | scriptable `reaper --judge` | `/reaper` (this skill) |
|---|---|---|
| Trigger | scheduled / CLI, `--live` to act | in-session slash command |
| Judgment | drives `/freshness-triage` headlessly (`claude -p`) | drives `/freshness-triage` in-session |
| Close disposition | acts under `--live` (unattended) | **review-first; closes only on explicit operator approval** |
| Default | mutate nothing without `--live` | **mutate nothing until you approve** |

This skill is **NOT** the cheap `SessionStart` dry-run hook (that runs `reaper`
with no flags and never judges — it costs no tokens). `/reaper` is on-demand,
costs tokens (one `/freshness-triage` pass per eligible candidate), and needs an
operator in the loop — that is the whole point of the review-first gate.

It is a **single-pass** triage: one `/freshness-triage` judgment per candidate,
one review table, one confirmation. It does not iterate or fan out.

## Scope boundary — what this skill does and does NOT do

- It judges whether a bead's **premise is still real** (via `/freshness-triage`)
  and offers genuinely-dead beads for close. It does **NOT** re-plan work, judge
  acceptance-criteria quality, or close a bead on idle-age alone.
- It **never** closes a bead the reaper exemptions protect, and **never** closes
  a bead with no positive deadness verdict — idle-age alone is advisory only
  (the **djinn-yume invariant**).
- It **mutates no bead state** — no `bd close`, no defer — until you explicitly
  approve the proposed closes (the confirmation gate, below).

## Process

### Step 1 — Gather the candidate set AND its exemption metadata (read-only)

Read the same idle candidate set the reaper tool reads, via `bd stale`:

```bash
bd stale -d 7 -s open -n 0 --json
```

`bd stale` takes **flags**, not positional args: `-d 7` is the idle-day window
(the reaper's `IdleThreshold`, 168h), `-s open` is the candidate status, and
`-n 0` lifts the default 50-row cap so the WHOLE idle set is triaged (the reaper
adapter passes `-n 0` for exactly this reason — `bd stale` otherwise truncates at
50). Mirror the production reaper defaults so `/reaper` triages the same set the
scriptable subcommand would. If the operator named a different window or status,
pass it through; otherwise use these defaults. An empty result means nothing is
idle — report "no idle candidates" and stop.

**`bd stale --json` is NOT sufficient by itself for Step 2.** Its records carry
only `id, title, description, design, acceptance_criteria, notes, status,
priority, issue_type, owner, created_at, created_by, updated_at` — **no `labels`
field and no `dependencies` field at all.** But the Step-2 exemption predicate
(below) needs each candidate's **labels** (for `reaper:keep` and `review-follow-up`)
and **dependency types** (for the `discovered-from` follow-up signal). So you MUST
enrich the stale set before filtering — do not try to apply the `reaper:keep`,
`review-follow-up`-grace, or `discovered-from` exemptions from `bd stale` output
alone (you cannot, and silently skipping them would offer an exempt bead for
close — an AC3 violation).

Enrich with one additional read-only query — `bd list --json` carries **both**
`labels` and `dependencies[]` (each dep entry has a `type` field, e.g.
`discovered-from`, `parent-child`):

```bash
bd list --json -n 0          # every bead, WITH labels + dependencies[].type
```

`-n 0` lifts `bd list`'s row cap the same way it does for `bd stale` (the default
caps well under the full backlog). **Join on `id`:** for each candidate the
`bd stale` set returned, look up the same `id` in the `bd list` output and attach
its `labels` array and its `dependencies[].type` set. A candidate's:

- **`reaper:keep` / `review-follow-up` exemption** reads from the joined `labels`.
- **`discovered-from` exemption** reads from the joined `dependencies[]` — the
  candidate is an auto-filed follow-up when any joined dep has
  `type == "discovered-from"` (`{"depends_on_id": "...", "type": "discovered-from"}`).
- **priority / `issue_type` / `updated_at`** are present in BOTH outputs; either
  copy is fine.

This is the documented enrichment join — `bd list --json -n 0` is the single
command that supplies the labels and dependency types `bd stale --json` omits, so
Step 2 can apply every exemption from in-band data with no out-of-band lookup.
(Equivalently, a per-candidate `bd show <id> --json` carries `labels` and a
`dependency_type` field; prefer the single `bd list` join over N `bd show` calls.)

### Step 2 — Apply the exemptions FIRST (AC3 — never offer an exempt bead for close)

Before judging anything, **filter out every exempt candidate**. This mirrors the
pure `reaper.JudgmentEligible` / `judgmentExempt` predicate
(`internal/djinn/reaper/judgment.go`) exactly — do NOT invent a different
exemption set. Read each candidate's `labels` and `dependencies[].type` from the
**Step-1 enrichment join** (`bd list --json -n 0`), NOT from the bare `bd stale`
record — the `reaper:keep`, `review-follow-up`, and `discovered-from` checks below
all depend on those joined fields, which `bd stale --json` does not carry. A
candidate is **exempt** (skip it; it is NEVER offered for close, it falls through
to *keep/advisory*) when ANY of these holds:

- It carries the **`reaper:keep`** label (the operator retention override) — read
  from the joined `labels`.
- It is **not age-reapable** (an iter bead / structurally protected bead —
  `beads.IsAgeReapable` is false).
- Its type is **`epic`**, or any type NOT in the reapable set
  (`task`, `feature`, `bug`, `chore`).
- Its priority is **above the floor** — `Priority < MinPriority` (default
  `MinPriority = 3`, so P0 / P1 / P2 beads are protected; only P3 / P4 are
  eligible).
- It is an **auto-filed follow-up** (joined `labels` contain `review-follow-up`,
  OR a joined `dependencies[]` entry has `type == "discovered-from"`) with a
  **non-zero `UpdatedAt`** still **within its grace
  window** — `now - UpdatedAt ≤ IdleThreshold + FollowupGrace` (168h + 14d). A
  follow-up PAST the grace window is no longer protected. **Caveat (mirrors
  `judgmentExempt`, `judgment.go:228`):** the grace check is gated on
  `&& !UpdatedAt.IsZero()` — a follow-up with a **ZERO `UpdatedAt`** (never
  updated) does NOT take the grace exemption; it falls through to non-exempt and
  is judged like any other candidate.

Surface the exempt candidates in the review table as **`keep (exempt: <which
rule>)`** so the operator sees they were considered and protected — do not
silently drop them. Only the NON-exempt candidates proceed to judgment, so no
token is spent judging a bead that can never be closed (a cost guard AND the AC3
correctness guarantee).

### Step 2b — Disclose the eligible-candidate count (cost preview)

Before firing any `/freshness-triage` call, report the count of **ELIGIBLE
(non-exempt) candidates** to the operator — judgment is **O(n)** in that count
(one `/freshness-triage` pass per eligible bead, each costing tokens). For a
large backlog (e.g. 250+ eligible) this discloses the token cost BEFORE it is
spent, so the operator can scope the window down (Step 1 flags) or proceed
knowingly. Keep it to one line, e.g. `42 eligible candidates to judge (42
/freshness-triage passes); proceeding.`

### Step 3 — Judge each eligible candidate via `/freshness-triage` (AC4 — reuse, do not reinvent)

For each NON-exempt candidate, drive the **`/freshness-triage`** skill against the
current worktree at HEAD — the SAME mechanism the scriptable `reaper --judge`
subcommand drives headlessly. Do **NOT** improvise a fresh judgment; invoke the
named procedure so the in-session and scriptable surfaces stay in lockstep. Pass
the candidate's bead id; `/freshness-triage` reads `bd show <id>` itself and emits
its fenced `verdict` sidecar.

Map each `/freshness-triage` verdict to a `/reaper` disposition exactly as the
scriptable edge's `parseFreshnessVerdict` does (`cmd/reaper/judge.go`):

| `/freshness-triage` verdict | `/reaper` disposition | Close reason |
|---|---|---|
| `fresh` | **keep** | (premise still real — not offered for close) |
| `already-done` | **close** (high confidence) | the sidecar `reason` (the retired/landed premise) |
| `obsolete` | **close** (high confidence) | the sidecar `reason` (the removed/redesigned premise) |
| (unrecognized / no fence) | **keep** | bias toward keep on any uncertainty |

Two guardrails carried over from the scriptable edge:

- **A dead verdict (`already-done`/`obsolete`) with NO `reason` is downgraded to
  keep** — a close must cite the retired premise (AC1); an empty reason cannot, so
  do not offer it for close.
- **Cross-check the sidecar's `bead` field against the candidate id.** If
  `/freshness-triage` emits a verdict whose `bead` names a DIFFERENT id (LLM
  confusion, or a crafted description), the verdict is **not attributable** —
  leave that candidate untouched (keep), do not close it on another bead's verdict.
  A **BLANK** sidecar `bead` is **tolerated** (the skill simply omitted it) — only
  a **present-and-different** id is non-attributable (mirrors `judge.go:168`:
  `sb != "" && sb != candidateID`).

A candidate `/freshness-triage` cannot judge (skill error, no parseable sidecar)
is left **untouched (keep)** with the failure noted — one un-judgeable bead never
aborts the pass and never becomes a close.

### Step 4 — Present the review table (AC1)

Present every candidate, grouped by disposition, with its cited reason:

```
CLOSE (genuinely dead — your approval required):
  djinn-abcd  P4  "Old foo migration"   — obsolete: foo subsystem removed in djinn-wxyz (commit 7ab9c28); the cited handler no longer exists at HEAD
  djinn-efgh  P3  "Duplicate bar task"  — already-done: bar shipped via djinn-1234; cited test TestBar passes at HEAD

KEEP (premise still real / exempt / no deadness signal):
  djinn-ijkl  P3  "Live feature work"   — fresh: the named endpoint still does not exist; work is real
  djinn-mnop  P2  "Important refactor"   — keep (exempt: P2 above MinPriority floor)
  djinn-qrst  P4  "reaper:keep roadmap"  — keep (exempt: reaper:keep label)
  djinn-uvwx  P4  "Idle but live"        — keep (no positive deadness signal; idle-age alone never closes — djinn-yume)
```

Each CLOSE row MUST name its cited HEAD evidence (the `/freshness-triage` reason).
A candidate with no positive deadness verdict appears under KEEP, never CLOSE —
this is the djinn-yume invariant made visible: idle-age alone never graduates to a
close offer.

### Step 5 — Confirmation gate (AC2 — close ONLY on explicit operator approval)

This is the load-bearing interactive affordance. After presenting the table:

- **Mutate NO bead state yet.** Up to this point `/reaper` has run only read-only
  commands (`bd stale`, `bd show`, `/freshness-triage`). Nothing has been closed.
- **Ask the operator to approve the CLOSE set explicitly.** Offer: approve **all**
  proposed closes, approve a **subset** (name the ids), or **none**. Do not
  proceed on silence, an ambiguous reply, or a generic "looks good" that does not
  clearly authorize closing — when in doubt, ask again. Default is **review-first:
  no close happens without an explicit yes.**
- **On approval, close ONLY the approved beads:**
  ```bash
  bd close <id> --reason "reaper content-judgment close: <the cited /freshness-triage reason>"
  ```
  Close exactly the approved ids — never a bead the operator did not name, never an
  exempt or keep bead. After the approved closes, persist the bead-state to the
  Dolt remote so collaborators see the closures:
  ```bash
  bd dolt commit -m "reaper: interactive content-judgment closes (<ids>)"
  bd dolt push
  ```
- **If the operator approves none**, mutate nothing and report that the triage was
  review-only — the candidates remain open for a future pass.

Report a final summary: which beads were closed (with their cited reasons), which
were kept, and which were exempt.

## Why this exists

The reaper's deterministic Tier-1 sweep (`PlanStaleness`) and the scheduled Tier-2
`reaper --judge` subcommand both act unattended. `/reaper` is the **operator's**
surface: a content-aware reaping pass an operator can run from within a session
and **review before any bead is closed**, seeing each keep/close verdict with
its cited HEAD evidence rather than hand-triaging the whole idle backlog. The
review-first confirmation gate is what makes it safe to run interactively — the
unattended `--live` subcommand acts on its own judgment; `/reaper` acts only on
yours. Driving the SAME `/freshness-triage` mechanism the scriptable subcommand
drives is what guarantees the two surfaces never disagree about whether a bead is
dead (AC4): one judgment, two triggers.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.