---
name: turn
description: Checks the skill repos compost was made from for upstream changes worth working in, and adds skill repos to watch as references. Use when asked "anything upstream worth pulling in?", "turn the pile", "check superpowers / Matt Pocock / pstack for updates", "add this skill repo as a reference", or on the monthly scheduled run.
---

# turn

Turning a compost pile mixes new material in. compost was made by breaking other people's skills
down and rebuilding them; those sources keep improving. `turn` finds what changed upstream since
compost last took from each source, decides what is worth working in, and files that as issues.
It never edits compost's skills itself: an adopted change goes through `compost:spec`, or
`compost:build` for a single small adopt, like any other work, so it gets built, verified, and
reviewed.

`pile.toml` at the plugin root lists every source:

- `input`: a source compost takes text from. `pin` is the upstream commit compost reflects, and
  `feeds` maps upstream paths to what they fed: a compost skill by name, `canon` for the essays, or
  a mechanism, `hooks` for compost's hooks as a whole or `hooks/<mod>` for one mod, so `pile.py
  status` reports an adopted mod's upstream drift as it does a skill's.
- `frozen`: an input that will not change again (a retired plugin). Listed for attribution only.
- `reference`: a repo watched for ideas and not drawn from yet.

`${CLAUDE_PLUGIN_ROOT}/scripts/pile.py` does the bookkeeping (Python 3.11+ via uv, stdlib only).

## Kinds and rulings

Both procedures below sort each upstream item by kind before judging it: a **skill** (a SKILL.md
and its references), a **mod or hook** (a hooks module, a hook entry, a hook script), an
**agent**, or a **script**. Judge a skill, agent, or script by its text. Judge a mod or hook as a
mechanism: name the event it fires on (a prompt submitted, a tool call, Stop, session start) and
the compost rule it would enforce that a skill now only states. Each item gets one ruling:

- **adopt**: better than compost's counterpart, fits as is (a sharper step, a fixed bug, a clearer
  example)
- **adapt**: the idea is good, the form conflicts with compost's rulings (a new approval gate,
  a TDD ritual, beads, a size rule); take the idea, not the text
- **add**: compost has no counterpart, and it serves a step in compost's workflow (spec, slice,
  build, verify, review, finish, pause, diagnose). Name the compost step it serves and what it
  makes cheaper or harder to skip. If no step fits, the ruling is ignore: it belongs in a
  standalone plugin, not in compost.
- **ignore**: churn, tooling specific to the source, or already covered

Adopt and adapt are overlaps, judged against the compost skill or mechanism the item feeds; add is
judged against the step it serves. Report the two apart.

## Check for upstream changes

1. In a checkout of the compost repo on its default branch, freshly pulled, run
   `uv run plugin/scripts/pile.py status --references`; the installed plugin's `pile.toml` lags
   until a release ships, so it would list changes an unreleased pin move already ruled on. For
   each input it keeps a blobless
   clone under `~/.cache/compost/upstream/`, lists every file changed since the pin, and groups
   them by the compost skill or mechanism they feed, then lists the names of the changed files no
   `feeds` entry maps. `--references` adds each reference source, whose changed files all feed
   nothing; that is how a watched repo gets watched. It prints the `git diff` command for reading
   each change.
2. Skim the unmapped file names for an item that did not exist when the pin was set: a skill, mod
   or hook, agent, or script. Note its kind. One that overlaps a compost skill or mechanism becomes
   a `feeds` entry now; a candidate add gets one once its issue lands, naming what it became. Its
   paths go into the sort below.
3. Sort the changes before reading them. Ask Jev first: per source, write
   `{"source": "<name>", "pile": "plugin/pile.toml", "paths": [<every path listed under a
   compost skill, plus promising unmapped skills>]}` to a temp file (`pile` points it at the
   checkout's pins, the ones `status` read) and run
   `uv run plugin/scripts/jev.py classify-change --input <file>`. Record each change
   with `needs_reading: false` as ignored, with Jev's verdict as the reason, without reading it.
   Read the diff of every path in `to_read` and judge it yourself; Jev's verdict there is a hint,
   since it over-calls adopt and adapt on long diffs ([Jev tools](references/jev-tools.md)). Exit 3
   means Jev is unavailable: read every listed diff. `classify-change` judges skill text against
   compost's skills and has no add verdict, so read every mod or hook and candidate add yourself.
   Give each change you read a ruling from [kinds and rulings](#kinds-and-rulings), against the
   [canon](../../canon/README.md). When dozens of changes remain to read, split them across
   parallel subagents, one per compost skill or mechanism, each returning a ruling with a one-line
   reason; then read the adopt, adapt, and add candidates yourself.
4. Before filing, list the open issues labelled `upstream` and skip any change one already
   carries (match on the upstream diff link): a run that died between filing and moving the pin
   leaves them behind. File one issue per remaining adopt, adapt, or add on the compost repo (the `repository` in
   `${CLAUDE_PLUGIN_ROOT}/.claude-plugin/plugin.json`), labeled `upstream`, with the upstream diff
   link, its kind, and what to take: for an overlap, the compost skill or mechanism it touches; for
   an add, the step it serves and what it makes cheaper or harder to skip.
5. Once every change since a pin has a ruling, move the pin. Pins live in the source repo, not the
   installed plugin: in a worktree of that repo run
   `uv run plugin/scripts/pile.py advance <source> <commit>`, with `<commit>` the full hash
   `status` printed for that source (a ref would resolve after a fresh fetch and could move the pin
   past changes nobody ruled on, so `advance` refuses one), then
   `uv run plugin/scripts/pile.py notice`, and commit both with the ignored changes and
   their reasons in the commit message. That is the record that keeps them from resurfacing.
6. Report per source, in two parts: overlaps, with how many changes were adopted, adapted, and
   ignored and the issue links; then additions, each add with its kind, the step it serves, and
   its issue link.

## Add a reference

1. Clone the repo and list what it ships by kind: skills, mods and hooks, agents, scripts. For the
   skills, when `skill-atlas` is installed, run `skill-atlas --claude-dir <clone> judge` then
   `report` to get each skill's category, craft, and the techniques it uses; otherwise read the
   SKILL.md files directly. Read each mod and hook for the event it fires on and what it does.
2. Give each item a ruling from [kinds and rulings](#kinds-and-rulings) and report overlaps and
   additions apart. For each overlap, name the compost skill or mechanism it overlaps and what it
   would bring that compost lacks: worked examples, a verification step, a decision procedure, a
   sharper trigger, a rule enforced instead of stated. For each add, name the step it serves and
   what it makes cheaper or harder to skip.
3. Add a `[[source]]` with `role = "reference"`, its license, and `pin` set to the commit you read.
   Later `status --references` runs list what it adds after that commit.
4. A reference becomes an `input` the first time compost takes text or a mod or hook from it: set
   the role, add `feeds` (a skill name, `hooks`, or `hooks/<mod>`), and regenerate NOTICE.

## Check compost's own text after a change

An adopted change rewrites a skill, and a description edit changes which skill Claude loads. After
any change to a skill's text or description, run the plugin's two regression evals before the
change merges ([Jev tools](references/jev-tools.md)):

1. **Routing.** The 30 labeled prompts live in the private compost-evals repo, as
   `$COMPOST_EVALS/route.json`, where `COMPOST_EVALS` points at that checkout's `evals/` directory.
   From `plugin` in the source repo, run
   `COMPOST_EVALS=<checkout>/evals uv run --group dev pytest -m jev tests/test_evals_meta.py -k route -s`.
   It routes every prompt through every skill's description and fails below 27 of 30. A new miss,
   or a prompt now marked `close`, points at the description that moved; fix the description, not
   the eval. Without a checkout the test skips rather than fails, so a skip is not a pass: say in
   the turn report that the routing eval did not run, and read the changed descriptions against
   the skills they compete with yourself.
2. **Rulings.** Write `{"paths": [<the changed markdown files, relative to the plugin root>]}` to a
   temp file and run `uv run ${CLAUDE_PLUGIN_ROOT}/scripts/jev.py rulings-lint --input <file>`
   (`{}` lints the whole plugin). Read each flagged passage against the rulings it names: reword
   the ones that drifted, and leave the false flags with a line in the commit message.

Exit 3 from either means Jev is unavailable: re-read the changed text against the rulings yourself.

## Run it on a schedule

A monthly run needs no one watching: it only reads upstream and files issues. Set it up with a
scheduled routine or a cron-driven `claude -p "/compost:turn"` in the compost repo.

## Next moves

- `compost:build` when `turn` filed a single small adopt worth doing now.
- `compost:spec` when filed issues are worth doing now: it turns them into a parent issue that
  `compost:implement` can work, and gives an adapted idea the design it needs.
