Agentic coding tools like Claude Code and Codex have a mechanism called Skills — a way to teach the agent your project's coding rules and the quirks of the libraries you use.
"Validation in this project is written like this."
"Debug this error in this order."
"That API has this particular trap."
Knowledge like that already exists on your team. But unless it becomes a Skill, the agent never notices it, and you end up explaining the same thing again in the next session.
So I built agent-skdd, a Claude Code plugin that catches that knowledge before it evaporates and harvests it into Skills that keep growing.
This post explains SkDD (Skill Driven Development), what the tool actually does, and why it is designed the way it is.
# Install
claude plugin marketplace add rabitarochan/agent-skdd
claude plugin install skdd@agent-skdd
Why project knowledge doesn't accumulate in AGENTS.md
The obvious way to hand project-specific knowledge to an LLM is to write it into AGENTS.md or CLAUDE.md. That is a perfectly good answer, and it works in plenty of cases.
It also runs into three problems.
-
Writing it down doesn't stick.
- You think "I should write that explanation down," but not enough to interrupt the work.
- Next time it comes up, you explain it out loud again.
-
The file bloats.
- Append everything to one
AGENTS.mdand the whole thing is loaded every session. - Knowledge irrelevant to today's task sits in context permanently, burying the part that matters.
- Append everything to one
-
The Why doesn't survive.
- A note written in a hurry captures the How and nothing else.
- "Throw ValidationException for validation failures" tells you what to do, but not what to do when the situation changes.
- Is it still a live rule, or a workaround for a constraint that no longer exists? You can't tell.
The third one is the real problem. A How that has lost its Why can only be obeyed — it cannot adapt as the project grows.
SkDD (Skill Driven Development) answers all three head-on.
- The agent writes. You only approve.
- The destination is a set of Skills, not one file — each loaded only when relevant.
- The unit of record is a How with its Why attached. A How without a Why is not accepted as a harvest.
What agent-skdd is
In one sentence: an ADR at skill granularity.
At each natural break in the work, the agent asks itself whether the exchange produced knowledge worth keeping. If it did, it proposes a Skill. Approve, and you get two files: a SKILL.md holding the procedure itself, and a harvest.md that accumulates the reasoning behind it.
An ADR records architectural decisions in chronological order. agent-skdd does the same thing, one skill at a time.
The name "skdd" stands for Skill Driven Development. Agentic development already has a pile of xDD names — SDD (Spec Driven Development) and friends — so I picked something that sits alongside them.
How it works
Once installed, proposals start coming from the agent's side at task boundaries.
📦 Skill Proposal
─────────────────────
Name: pj-api-error-routing
Location: project (.claude/skills/)
Summary: Route domain-consistency check failures into the existing validation path
Scope: - How to implement external-system reference checks
- When to use ValidationException
- Constraint: do not add new frontend handling
Trigger: "I want to add a domain check" / "How should this error surface?"
Criteria met: recurrence, proceduralness, non-obviousness, correction-derived
Threshold: medium (bar: 3/5)
Overlap: Checked pj-validation-basics — that one covers input-format validation
only, not the external-reference decision
─────────────────────
Shall I generate this?
Approve, and SKILL.md and harvest.md appear under .claude/skills/pj-api-error-routing/, ready to commit. From the next session onward, that skill loads automatically whenever the work is related.
Whether something gets picked up is decided against five criteria.
- Recurrence — is this likely to come up again?
- Proceduralness — is there a reproducible procedure?
- Non-obviousness — is it something you couldn't derive from the docs or common sense?
- Correction-derived — did it come from the user correcting the agent, or from trial and error?
- Generality — does it apply beyond the one file or one ticket in front of you?
Of these, correction-derived is the one that earns its place. The spot where a user corrects the agent is where tacit knowledge shows up at its highest concentration.
Work the agent got right on its own rarely contains anything worth recording. The value is in the path that got stuck, got corrected, and then worked.
One dial controls how much you harvest
How greedily the agent picks things up comes down to a single setting: the harvest threshold.
| Level | Propose at | Proto-Skill | Promotion | Consolidation bias | Line cap |
|---|---|---|---|---|---|
low |
2/5+ | 1/5 | 2 sessions | Create new unless an existing skill clearly covers it | 500 |
medium |
3/5+ | 1-2/5 | 2 sessions | Update when scopes overlap | 500 |
high |
4/5+ | 2-3/5 | 3 sessions | Read every existing description first; update on overlap | 200 |
max |
5/5 | 3-4/5 | 4 sessions | Must state why no existing skill can absorb it | 120 |
Raising the level tightens three things at once.
- The score bar — how many criteria must be met to propose.
- Consolidation bias — how hard to prefer updating an existing skill over creating a new one.
-
Distillation discipline — the line cap on
SKILL.md.
Why a dial? Because the right harvest rate is not the same across projects.
When you have zero skills, pick things up aggressively. On a mature project the incentive inverts: the more skills you have, the more diluted the set becomes, and the harder it is to surface the one that matters. Adding the 21st skill to a set of 20 costs nothing like adding the first to a set of zero.
The distillation rule at max captures the intent:
Transcribing the steps you just performed is not harvesting — state the rule
that would let a future agent derive those steps.
You pick the threshold during /skdd:setup and change it later with /skdd:config threshold=high. The default is medium.
Knowledge below the bar isn't thrown away
Anything that misses the bar but still looks like knowledge is recorded in backlog.md as a Proto-Skill — a seed.
If the same topic resurfaces within the configured number of sessions, promotion is proposed:
"The note I made earlier about <topic> came up again this session. It might be worth turning into a skill. Shall I?"
Whether a one-off turns out to have recurrence is something you can only know in hindsight. Having somewhere to defer the decision means you can set a high threshold without losing as much.
backlog.md is per-developer local state and is gitignored — nobody else's half-formed hunches end up in your repository.
SKILL.md + harvest.md — an ADR per skill
This is the core of SkDD. Every harvested skill is managed as a pair of files.
.claude/skills/pj-api-error-routing/
├── SKILL.md # current Why + How snapshot
└── harvest.md # append-only decision log (ADR)
| Contents | When it's read | |
|---|---|---|
SKILL.md |
Live Why — why the current procedure is correct | Loaded at runtime |
harvest.md |
Retired Why — what was tried, and why it was dropped | Not normally loaded; only during harvesting and review |
Why split them?
The live Why has to influence execution, so it must sit on the side that gets loaded at runtime. What lets an agent adapt instead of blindly following a procedure — when the project's situation has shifted — is having the reasoning right there.
The retired Why is noise at that moment. But delete it and you lose the lineage of decisions. "Why aren't we doing it that way?" becomes unanswerable, and discarded options get relitigated indefinitely.
Splitting them keeps SKILL.md lean and harvest.md complete.
Atomic update
A decision-level change is one transaction.
① Draft the harvest.md entry FIRST
② Edit SKILL.md (including the inline Why)
③ Append the entry to harvest.md
④ Verify the invariant
Changing only one side is invalid. You do not end the task partway through the sequence.
Writing down why you're changing something before you change it is itself the quality gate. It stops decisions from being silently overwritten.
There is exactly one invariant to hold:
The last entry in harvest.md == the rationale for the current SKILL.md
While that holds, "why is this skill shaped the way it is?" is answered by reading the tail of one file. No diffing through git history.
If the invariant is broken — SKILL.md changed on its own — the agent reconstructs an entry marked context: drift repair and appends it. There's a defined escape hatch so a break never gets silently ignored.
A real harvest.md
# Harvest Log — pj-api-error-routing
## 001: Route domain-check failures through the validation pipeline
- date: 2026-06-01
- context: External-system reference checks could not be expressed in the
standard validator, so errors surfaced through an ad-hoc exception with its
own frontend handling, diverging from every other error path.
- change: Domain checkers now throw the shared ValidationException so failures
join the existing validator display path. Why: one error pipeline is easier
to reason about than two; How: build ValidationResult manually and throw.
- supersedes: —
- result: Reused across 3 screens in the first week without new frontend code.
## 002: Prefer warning-response over exception for save-time checks
- date: 2026-06-15
- context: A new requirement wanted saves to succeed while still surfacing
check failures; blocking via exception (001) prevented that.
- change: Save-time checks return warning messages on the response instead of
throwing; blocking exceptions remain only for confirm-time checks. The 001
approach is retired for save paths but stays valid for confirm paths.
- supersedes: 001
- result:
context holds the situation that forced a rethink, not the change itself. What a future reader needs in order to judge "is this decision still valid?" is the former, not the latter.
result is the one field that may be rewritten later. Fill it in once you know how it went.
Oscillation guard
Projects move. An approach you rejected six months ago starts looking attractive again once the reasons have faded.
So before adopting an approach in a skill update, there's a rule to scan harvest.md for an entry that already rejected it.
If one turns up, the agent doesn't quietly re-adopt it. Either the rejection stands, or — if the context genuinely changed — it explicitly overrides that entry via supersedes and states what changed that justifies the reversal.
This stops the thrash of re-adopting discarded ideas with a confident face.
Entry 002 above is exactly that. It doesn't negate 001; it splits the scope — retired for save paths, still valid for confirm paths.
Deliberately not owning the skill format
SkDD splits the rules for generated skills into two layers.
-
Layer 1 — SkDD invariants
- The five criteria, the per-level bars and line caps, the
SKILL.md+harvest.mdpair and atomic update, naming and routing, the oscillation guard, never renaming a skill. - Hardcoded into the engine. Always wins.
- The five criteria, the per-level bars and line caps, the
-
Layer 2 — platform conventions
- Frontmatter fields, how to write a description, the shape of the body, the flavour of progressive disclosure (when to split into
references/). - Not baked into the plugin. Resolved on the spot, at every harvest.
- Frontmatter fields, how to write a description, the shape of the body, the flavour of progressive disclosure (when to split into
The resolution chain:
1. If a skill-authoring skill is in session (e.g. Anthropic's skill-creator), follow it
2. Otherwise, use the model's own latest knowledge of Skills best practices
3. If neither is confidently newer than the baseline,
use the dated baseline shipped with the engine (as of 2026-08)
Precedence: SkDD invariants > newer platform conventions > dated baseline
No documentation is fetched. The chain has to work offline.
The point is that as models and Claude Code evolve, harvested skills track current best practice without the plugin being updated.
If the tool froze "the correct way to write a SKILL.md as of 2026-08" inside itself, it would start rotting the moment it was written. So it doesn't own the format. It hardcodes only the judgment it should actually own: what to harvest, when, and how strictly.
The "if you aren't sure, use the baseline" precedence is deliberate too. A misremembered "newer" convention is worse than a real one with an old date on it.
When updating an existing skill, newer conventions apply only to the parts you touched. A full rewrite for style consistency counts as churn, not growth.
Setup and operation
Install
The plugin itself is installed globally, once.
claude plugin marketplace add rabitarochan/agent-skdd
claude plugin install skdd@agent-skdd
Then run setup in each project where you want skdd active.
cd ~/work/my-project && claude
> /skdd:setup
Setup asks three things.
-
Skill prefix — defaults to
pj-. Project skills are namedpj-<domain>-<action>. - Whether to install the Stop hook — prompts a harvest evaluation each time a response completes.
-
Harvest threshold — defaults to
medium.
Here's what gets deployed.
| Path | Role | Managed by update? |
|---|---|---|
.claude/skills/skdd-harvest/SKILL.md + references/
|
Harvest engine | Yes (overwritten) |
.claude/skills/skdd-harvest/backlog.md |
Proto-Skill backlog | No — per-developer state |
.claude/hooks/skdd-stop.sh + the Stop entry in settings.json
|
Harvest reminder (opt-in) | Yes |
The managed section of AGENTS.md
|
Protocol for every agent | Yes (re-rendered) |
The @AGENTS.md line in CLAUDE.md
|
Bridge for Claude Code | Repaired if missing |
Everything except backlog.md is meant to be committed.
Three commands
/skdd:setup # install into a project
/skdd:config threshold=high # change settings (threshold, prefix, hook)
/skdd:update # after upgrading the plugin, bring deployed assets forward
/skdd:config changes settings; /skdd:update changes the version. config takes arguments, and without them it walks you through the settings interactively.
The two are independent on purpose: raising the threshold never swaps the engine out from under you.
Neither ever touches backlog.md or your harvested skills.
Why setup runs per project
Most plugins work the moment you install them. agent-skdd only starts working after you install it and deploy files into a project. That's extra friction, and there are three reasons I accepted it.
1. The knowledge is project-specific.
The harvest protocol — what to pick up, when, how strictly — can be identical everywhere. The knowledge it picks up, the prefix, and the threshold cannot. The former lives in the plugin; the latter lives in your repository.
2. The output becomes part of the repository.
Harvested skills are born in .claude/skills/pj-*/, get committed, get reviewed, and get shared with the team. Locked inside a global install, they'd be nothing but personal notes.
3. Agents other than Claude Code can participate.
What gets deployed is plain Markdown and a shell script. Any other agent that reads AGENTS.md — OpenAI Codex, or anything else Agent Skills-compatible — follows the same protocol with no plugin installed at all. The plugin is a Claude Code feature; the output isn't confined to Claude Code.
Teammates benefit without knowing the plugin exists. Clone the repository and the harvested rules and skills are simply there. Only the person installing and upgrading needs the plugin at all.
There's a trade-off. Upgrading the plugin does not update project assets automatically. Nothing about your agent's rules changes overnight in a repository that's about to ship — but you do have to run /skdd:update when you want the new version.
That question, "how do you keep updating something you've already distributed?", turned into the main design problem of the plugin. The answer: carve out a managed region inside AGENTS.md with <!-- skdd:begin --> / <!-- skdd:end --> markers, and keep the settings and deployed version in a single HTML comment just inside them.
Current limitations
Being honest about where this stands:
-
Local edits to a deployed
skdd-harvestget overwritten. Engine improvements are meant to happen on the plugin side. - Changing the prefix does not rename existing skills. You migrate those by hand.
- The threshold only changes what gets harvested from now on. It does not revisit or reorganise skills already harvested at a lower bar.
- Newer platform conventions likewise apply only going forward. Updates touch only what you edited, so existing skills are never restyled wholesale. That's intentional, but it does mean older-format skills stick around.
Wrapping up
agent-skdd grows the knowledge that emerges while working with an LLM into Skills that carry their Why.
What SkDD argues, reduced to one line:
Preserve the How together with its Why.
Keep only the procedure and nobody can make a call when the situation changes. Keep only the reasoning and you rebuild the procedure from scratch every time. You need both — and you need the current decision kept separate from the retired ones. That's what the SKILL.md + harvest.md pair is for.
And rather than relying on a human to write all that down, it falls out as a by-product of working with the agent. That, I think, is what finally makes it sustainable.
claude plugin marketplace add rabitarochan/agent-skdd
claude plugin install skdd@agent-skdd
- Repository: https://github.com/rabitarochan/agent-skdd
It's still rough in places. If you've ever caught yourself thinking "I've explained this before," it might be worth a try. Feedback and issues are very welcome.
Top comments (0)