Pull generic improvements from a downstream project back into the /harness source tree. Detects the source project's stack (Go, Java Spring Boot, or the generic fallback) and diffs its harness runtime (.claude/, schemas, scripts) against the materialized harness (core plus the stack slice), plus the Language Realization section of each brief whose stack ships a fragment. Classifies each change as harvest, skip, or ask, generalizes domain patterns on the way back, and routes language-agnostic ...
Installs into .claude/skills of the current project.
Are you the author of Harvest?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/woditschka-harvest)
---
name: harvest
description: >-
Pull generic improvements from a downstream project back into the /harness
source tree. Detects the source project's stack (Go, Java Spring Boot, or the generic fallback)
and diffs its harness runtime (.claude/, schemas, scripts) against the
materialized harness (core plus the stack slice), plus the Language
Realization section of each brief whose stack ships a fragment. Classifies
each change as harvest, skip, or ask, generalizes domain patterns on the way
back, and routes language-agnostic changes to core/, stack-specific ones to
stacks/<stack>/, and realization rules to the stack's fragment. Load when
the user invokes `/harvest <project-path>`.
compatibility:
- claude-code
metadata:
version: "3.0"
author: team
---
# Harvest
Pull generic improvements from a real project back into the `/harness` source tree. Runs from the monorepo root. `/harness` is the single canonical runtime: `core/` holds the files every stack shares byte-for-byte; `stacks/<stack>/` holds the stack-specific ones. Harvest writes each improvement to the layer it belongs to; the samples and any other consumer pick it up on the next `materialize`.
**Usage:** `/harvest <project-path>` (e.g., `/harvest ../home-status-page`)
## Stack Selection
Detect the source project's stack the same way `init` and `materialize` do: the marker table lives in `/init`'s "Precondition" section; the code home is `detect_stack` in `harness/registry.py`. More than one marker → ask. Below, `<stack>` is the detected stack.
The **comparison target** is the materialized harness for that stack — `core/` overlaid with `stacks/<stack>/`. Each runtime file resolves to `harness/core/<path>` when present there, otherwise `harness/stacks/<stack>/<path>`. A diff against that overlay is what a materialized consumer of this stack would see.
## What to Compare
Compare the source project's harness runtime against the materialized harness (core ∪ `stacks/<stack>`) for each category.
Source projects may be set up with any subset of the three supported tools (the `init` skill selects them via `[harness] tools`). For each category below, if the source project does not have the path, skip it — a partial-tool downstream is valid and not a harvest signal.
Read `[harness] channel` from the source's `scripts/layout.toml` first. On the marketplace channel only the engine sliver lives in the tree (`scripts/`, `schemas/scratch/`, `.claude/templates/`) — compare only those categories. The brief realization row is channel-independent, because every channel carries `docs/`. Every absent skill, agent, or hook reflects the channel, not a deletion.
| Category | Source | Harness (core ∪ stacks/<stack>) |
|---|---|---|
| Skills | `<project>/.claude/skills/` (whole tree: SKILL.md, carried reference docs, doctor templates and manifest, engines) | `.claude/skills/` |
| Claude Code agents | `<project>/.claude/agents/*.md` | `.claude/agents/*.md` |
| Copilot agents | `<project>/.github/agents/*.agent.md` | `.github/agents/*.agent.md` |
| OpenCode agents | `<project>/.opencode/agents/*.md` | `.opencode/agents/*.md` |
| Templates | `<project>/.claude/templates/*.md` | `.claude/templates/*.md` |
| Hooks | `<project>/.claude/hooks/*.py` (hooks and their test siblings) | `.claude/hooks/*.py` |
| Scratch schemas | `<project>/schemas/scratch/*.json` | `schemas/scratch/*.json` |
| Harness scripts | `<project>/scripts/*.py` | `scripts/*.py` |
| Rules | `<project>/CLAUDE.md` | *(stack-specific; compare against `stacks/<stack>` only if carried there)* |
| Brief realization | `<project>/docs/<brief>.md` § Language Realization | `.claude/skills/doctor/templates/<brief>.realization.md` under `stacks/<stack>`, where the stack ships one; no fragment, no comparison |
The source project's `docs/` is **not compared file-to-file** — briefs are project-owned and their divergence from the harness defaults is the design, not drift. Harvest signals still come from there; route them by the table below. The brief realization row is section-level: a project's `## Language Realization` against the fragment its stack ships. That section is the one place a stack default lives in a brief.
`settings.json` (agent-teams flag plus hook registration) and `settings.local.json` (permission list) are project-owned config in the manifest channel, not materialized runtime — they are not harvested.
## Routing by Kind
Every harvested change lands in the harness layer it belongs to. Core vs stack is the central decision: **does this change apply to every stack, or only this one?**
| Kind of insight | Destination |
|---|---|
| Harness mechanics, language-agnostic (skill process, cross-stack agent structure, script logic, schema shape) | `harness/core/` — every stack inherits it on the next materialize |
| Harness mechanics, stack-specific (lint rules, build commands, naming regexes, a stack's agent prose) | `harness/stacks/<stack>/` |
| Policy insight (a better *default* value, section, or wording for a brief) | `harness/core/.claude/skills/doctor/templates/` — the shipped defaults; consumer briefs are never touched |
| Stack realization insight (a placement rule, a stereotype mapping, a package convention that holds for every project on the stack) | `harness/stacks/<stack>/.claude/skills/doctor/templates/<brief>.realization.md` — the stack's fragment. A fragment edit trips the battery's realization pin until the stack sample's section is copied from it. A change of the shipped default's stance is the maintainer's call and lands with a root ADR |
| Project-specific content | Stays downstream; not harvested |
| Kernel- or spec-level change (roster, required sections, ownership or channel rule) | Root ADR + `docs/harness-project-api.md` + spec version bump — never a silent core edit |
| Generic harness *decision* recorded downstream | Root `docs/adr/` (the handbook decision log) — consumers no longer ship harness ADRs |
**Layer moves.** A change may not match where the file currently lives. If a file in `core/` gains a stack-specific change, that fact must split out to `stacks/<stack>/` (the core copy stays shared); if a file in both stacks converges, it may lift to `core/`. Note any such move explicitly in the report — it changes what every other stack sees.
## Classification Rules
For every difference found, classify it. Decide by one principle: harvest what generalizes across projects, skip what encodes one project's domain, and ask when generic structure and domain detail are entangled. The buckets below list the common cases; when a diff matches none, fall back to that principle rather than pattern-matching the examples.
### Generic (harvest into the harness)
- New skill not in the harness
- New section added to an existing skill (e.g., a new checklist category)
- Structural improvement to an agent (new section, better process steps, added tool)
- New template file
- Bugfix or improvement in a harness script
- Improved wording that isn't domain-specific
- A rule in a brief's `## Language Realization` that holds for every project on the stack: a placement rule, a stereotype mapping, a sub-package convention. Generalize it on the way back: the rule stays, the project's own type and module names go
### Domain-Specific (do NOT harvest)
- A downstream-only path covered by `harness/retired-paths.txt` — a stale orphan the project never pruned, not a new unit; skip it and recommend pruning
- Filled-in `<!-- PROJECT -->` comment blocks in any runtime file
- Requirement IDs with real scope prefixes (`REQ-DL-*`, `REQ-SP-*` — the harness uses `REQ-XX-*`)
- Project name replacing `{{PROJECT_NAME}}`
- Specific file paths (e.g. `internal/render/render.go` or `src/main/java/com/example/render/Render.java` — generalize to placeholder style)
- Threat models referencing specific technologies (WebSocket, gRPC, etc.)
- Specific container/deployment details
- References to project-specific config fields
- Trimmed tool-surface prose (a claude-only downstream dropping cross-tool references is its opt-out, not an improvement)
- Realization prose that only names the project's own types, modules, or worked cases — the rule behind it may harvest; the example never does
### Ambiguous (ask the user)
- Content that mixes generic structure with domain examples
- A realization rule that takes a different stance from the shipped fragment (another stereotype mapping, another package convention). A preference, not a defect; adopting it changes the default for every consumer on the stack
- Changes to existing wording where intent is unclear
- Removed sections (might be intentional cleanup or accidental)
### Deleted in Source (ask the user)
A file that exists in the harness but **does not exist** in the source project. Two valid causes:
- **Intentional deletion** — the source project replaced the file with something better (e.g., migrated a markdown handoff template to a JSON Schema in `schemas/scratch/`). The harness should drop the file too.
- **Out-of-scope omission** — the source project never adopted the file (e.g., a doc the user did not need). The harness keeps it.
Procedure: list every harness-only file alongside the harvest report. For each, ask the user "delete from harness, keep, or skip this category". Default to keep when unsure — deletion is irreversible from harvest's perspective. A deletion in `core/` removes the file for every stack; a deletion in `stacks/<stack>/` affects only that stack.
## Process
1. Read the source project path from the argument: `$ARGUMENTS`
2. Verify the source project exists and has a `.claude/` directory; detect `<stack>` per Stack Selection.
3. For each category in the table above, diff the source against the materialized harness (core ∪ `stacks/<stack>`). For the brief realization row, diff the section body only, and only where the stack ships a fragment.
4. **Detect deletions:** for each harness file in every category, check whether the source has the same file. Files present in the harness but missing in source are candidates for "Deleted in Source". The brief realization row is exempt: a brief without the section, or one still under an older heading, is materialize's outward proposal, never a fragment-deletion candidate.
5. Classify every difference using the rules above; for each, decide its destination layer (core vs stack) per Routing by Kind.
6. Present findings to the user in four groups:
- **Harvest** — generic improvements to apply, each tagged with its destination layer. Show the diff.
- **Skip** — domain-specific content. List briefly with reason.
- **Ask** — ambiguous changes. Show the diff and ask for a decision.
- **Deleted in Source** — harness-only files. For each, ask delete / keep / skip-category.
7. Wait for user confirmation before applying any changes.
8. Apply confirmed changes to the harness, each to its destination layer: language-agnostic → `harness/core/`; stack-specific → `harness/stacks/<stack>/`. Perform any layer move noted in step 5. There is no cross-sample copying — `core/` is the one shared place.
9. **Verify:** re-materialize the affected samples (`harness/materialize-samples.sh`) and run their doctors and script-test suites to confirm the harvest did not break a stack. A change applied to `core/` must leave *both* stacks passing. After a fragment change, copy the fragment verbatim into the stack sample's `## Language Realization` section; the battery's realization pin fails until they match.
## Generalization Rules
When harvesting, transform domain content to harness form:
| Domain Pattern | Harness Form |
|---|---|
| `home-status-page`, `dirigera-exporter`, etc. | `{{PROJECT_NAME}}` |
| `REQ-DL-001`, `REQ-SP-002`, etc. | `REQ-XX-001` |
| `internal/render/render.go:87` (Go source) | A placeholder path (e.g. `internal/example/handler.go:87`) |
| `src/main/java/com/example/render/Render.java:87` (Java source) | `src/main/java/com/example/project/{package}/{Class}.java:87` |
| `make security` (Go target with govulncheck) | `go mod verify` with govulncheck as optional |
| Project-specific responses (`GitHub API responses`) | `External responses` |
| `valid_outlet.json` | `valid_input.json` |
| `ParseDevice` | `ParseInput` |
| `NNN-short-title.md` (project ADR convention) | `YYYY-MM-DD-title-in-kebab-case.md` (default) |
If a new pattern appears that isn't in this table, ask the user how to generalize it.
### JSON Schema Files (`schemas/scratch/*.json`)
Schemas carry both **structural** content (record types, required-field lists, the shape of `properties` and `items`) and **language-specific** content (regex patterns, naming conventions, example values). The structural layer is shared (`core/schemas/`); the language-specific layer is stack-owned (`stacks/<stack>/schemas/`). Harvest each accordingly:
| Schema Element | Treatment |
|---|---|
| `required` field list | Harvest to `core/` — structural |
| `properties` keys | Harvest to `core/` — structural |
| Field-level `description` | Harvest to `core/` — generic prose |
| `enum` values for agent names (e.g. `"product-requirements-expert"`) | Harvest to `core/` — these match agent files |
| `$id` namespace (e.g. `ccledger://...`) | **Skip** — each project owns its namespace |
| Regex `pattern` constrained to one language (e.g. Go `^Test[A-Z]`, JUnit `@Test`-tagged method names) | Route to `stacks/<stack>/` — the stack owns its language-specific patterns |
| Path-shaped strings in examples | Generalize per the path rows above |
| File `description` mentioning specific tooling (e.g. `make ci`, `./gradlew test`) | Prefer language-neutral wording; otherwise keep in the stack |
A schema that is structurally identical across stacks lives in `core/`; one that embeds a stack pattern lives in `stacks/<stack>/`. If a `core/` schema gains a language-specific pattern, split that schema to the stacks (see Layer moves).
### Harness Scripts (`scripts/*.py`)
The engines `handoff.py`, `grading.py`, and `changeset.py` live in `core/scripts/`, their suites under `core/scripts/tests/` — shared by every stack. A logic improvement to any of them is harvested to `core/`. The real-layout grading suites (`tests/grading/test_config_layout.py`, `test_features_layout.py`) are layout-coupled and live per stack (`stacks/<stack>/scripts/tests/grading/`): harvest logic changes, never a downstream's fixture paths or sensitive-path cases. `scripts/layout.toml` is project configuration: never harvest its module rules. After a `core/` script change, run every stack's script-test suite via re-materialize, not just the source stack's.
### ADR Files (`docs/adr/*.md`)
The downstream decision log is project-owned and is not diffed against the harness. Harvest a downstream ADR only when it records a *generic harness* decision (e.g. "use append-only JSONL handoffs") — and route it to the **root** `docs/adr/` handbook log, generalized, per the Routing by Kind table. Skip project-specific decisions. When in doubt, classify as Ask. Preserve the original date when copying — do not retroactively re-date.
## Output Format
```
## Harvest Report: <project-name> (stack: <stack>)
### Harvest (generic improvements)
1. **[category] file → core | stacks/<stack>** — description of change
```diff
...
```
### Skip (domain-specific)
- **file** — reason (e.g., "filled-in PROJECT block")
### Ask (ambiguous)
1. **file** — description. Harvest or skip?
### Deleted in Source (harness-only files)
1. **[category] file (core | stacks/<stack>)** — present in harness, missing in source.
Possible cause: <inferred>.
Action: delete / keep / skip-category?
### Summary
- X changes to harvest (C to core, S to stacks/<stack>)
- Y domain-specific skipped
- Z need your decision
- D harness-only files awaiting delete / keep
```