From 4df960c9d5b58f27fb5e617f5da452ec63e9ae4e Mon Sep 17 00:00:00 2001 From: Neha Prasad Date: Mon, 23 Mar 2026 04:09:48 +0530 Subject: [PATCH] feat: define skill placement and provenance policy (#748) --- CLAUDE.md | 1 + docs/SKILL-PLACEMENT-POLICY.md | 104 +++++++++++++++++++++++ schemas/provenance.schema.json | 31 +++++++ scripts/ci/validate-commands.js | 9 +- scripts/ci/validate-install-manifests.js | 3 + scripts/ci/validate-skills.js | 6 +- 6 files changed, 148 insertions(+), 6 deletions(-) create mode 100644 docs/SKILL-PLACEMENT-POLICY.md create mode 100644 schemas/provenance.schema.json diff --git a/CLAUDE.md b/CLAUDE.md index 02656ea2..10064cc6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -47,6 +47,7 @@ The project is organized into several core components: - Cross-platform: Windows, macOS, Linux support via Node.js scripts - Agent format: Markdown with YAML frontmatter (name, description, tools, model) - Skill format: Markdown with clear sections for when to use, how it works, examples +- Skill placement: Curated in skills/; generated/imported under ~/.claude/skills/. See docs/SKILL-PLACEMENT-POLICY.md - Hook format: JSON with matcher conditions and command/notification hooks ## Contributing diff --git a/docs/SKILL-PLACEMENT-POLICY.md b/docs/SKILL-PLACEMENT-POLICY.md new file mode 100644 index 00000000..7f11f74b --- /dev/null +++ b/docs/SKILL-PLACEMENT-POLICY.md @@ -0,0 +1,104 @@ +# Skill Placement and Provenance Policy + +This document defines where generated, imported, and curated skills belong, how they are identified, and what gets shipped. + +## Skill Types and Placement + +| Type | Root Path | Shipped | Provenance | +|------|-----------|---------|------------| +| Curated | `skills/` (repo) | Yes | Not required | +| Learned | `~/.claude/skills/learned/` | No | Required | +| Imported | `~/.claude/skills/imported/` | No | Required | +| Evolved | `~/.claude/homunculus/evolved/skills/` (global) or `projects//evolved/skills/` (per-project) | No | Inherits from instinct source | + +Curated skills live in the repo under `skills/`. Install manifests reference only curated paths. Generated and imported skills live under the user home directory and are never shipped. + +## Curated Skills + +Location: `skills//` with `SKILL.md` at root. + +- Included in `manifests/install-modules.json` paths. +- Validated by `scripts/ci/validate-skills.js`. +- No provenance file. Use `origin` in SKILL.md frontmatter (ECC, community) for attribution. + +## Learned Skills + +Location: `~/.claude/skills/learned//`. + +Created by continuous-learning (evaluate-session hook, /learn command). Default path is configurable via `skills/continuous-learning/config.json` → `learned_skills_path`. + +- Not in repo. Not shipped. +- Must have `.provenance.json` sibling to `SKILL.md`. +- Loaded at runtime when directory exists. + +## Imported Skills + +Location: `~/.claude/skills/imported//`. + +User-installed skills from external sources (URL, file copy, etc.). No automated importer exists yet; placement is by convention. + +- Not in repo. Not shipped. +- Must have `.provenance.json` sibling to `SKILL.md`. + +## Evolved Skills (Continuous Learning v2) + +Location: `~/.claude/homunculus/evolved/skills/` (global) or `~/.claude/homunculus/projects//evolved/skills/` (per-project). + +Generated by instinct-cli evolve from clustered instincts. Separate system from learned/imported. + +- Not in repo. Not shipped. +- Provenance inherited from source instincts; no separate `.provenance.json` required. + +## Provenance Metadata + +Required for learned and imported skills. File: `.provenance.json` in the skill directory. + +Required fields: + +| Field | Type | Description | +|-------|------|-------------| +| source | string | Origin (URL, path, or identifier) | +| created_at | string | ISO 8601 timestamp | +| confidence | number | 0–1 | +| author | string | Who or what produced the skill | + +Schema: `schemas/provenance.schema.json`. Validation: `scripts/lib/skill-evolution/provenance.js` → `validateProvenance`. + +## Validator Behavior + +### validate-skills.js + +Scope: Curated skills only (`skills/` in repo). + +- If `skills/` does not exist: exit 0 (nothing to validate). +- For each subdirectory: must contain `SKILL.md`, non-empty. +- Does not touch learned/imported/evolved roots. + +### validate-install-manifests.js + +Scope: Curated paths only. All `paths` in modules must exist in the repo. + +- Generated/imported roots are out of scope. No manifest references them. +- Missing path → error. No optional-path handling. + +### Scripts That Use Generated Roots + +`scripts/skills-health.js`, `scripts/lib/skill-evolution/health.js`, session hooks: they probe `~/.claude/skills/learned` and `~/.claude/skills/imported`. Missing directories are treated as empty; no errors. + +## Publishable vs Local-Only + +| Publishable | Local-Only | +|-------------|------------| +| `skills/*` (curated) | `~/.claude/skills/learned/*` | +| | `~/.claude/skills/imported/*` | +| | `~/.claude/homunculus/**/evolved/**` | + +Only curated skills appear in install manifests and get copied during install. + +## Implementation Roadmap + +1. Policy document and provenance schema (this change). +2. Add provenance validation to learned-skill write paths (evaluate-session, /learn output) so new learned skills always get `.provenance.json`. +3. Update instinct-cli evolve to write optional provenance when generating evolved skills. +4. Add `scripts/validate-provenance.js` to CI for any repo paths that must not contain learned/imported content (if needed). +5. Document learned/imported roots in CONTRIBUTING.md or user docs so contributors know not to commit them. diff --git a/schemas/provenance.schema.json b/schemas/provenance.schema.json new file mode 100644 index 00000000..01dadf70 --- /dev/null +++ b/schemas/provenance.schema.json @@ -0,0 +1,31 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Skill Provenance", + "description": "Provenance metadata for learned and imported skills. Required in ~/.claude/skills/learned/* and ~/.claude/skills/imported/*", + "type": "object", + "properties": { + "source": { + "type": "string", + "minLength": 1, + "description": "Origin (URL, path, or identifier)" + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "ISO 8601 timestamp" + }, + "confidence": { + "type": "number", + "minimum": 0, + "maximum": 1, + "description": "Confidence score 0-1" + }, + "author": { + "type": "string", + "minLength": 1, + "description": "Who or what produced the skill" + } + }, + "required": ["source", "created_at", "confidence", "author"], + "additionalProperties": true +} diff --git a/scripts/ci/validate-commands.js b/scripts/ci/validate-commands.js index c5695439..1ca5ae49 100644 --- a/scripts/ci/validate-commands.js +++ b/scripts/ci/validate-commands.js @@ -99,13 +99,14 @@ function validateCommands() { } // Check skill directory references (e.g., "skills/tdd-workflow/") + // learned and imported are reserved roots (~/.claude/skills/); no local dir expected + const reservedSkillRoots = new Set(['learned', 'imported']); const skillRefs = contentNoCodeBlocks.matchAll(/skills\/([a-z][-a-z0-9]*)\//g); for (const match of skillRefs) { const refName = match[1]; - if (!validSkills.has(refName)) { - console.warn(`WARN: ${file} - references skill directory skills/${refName}/ (not found locally)`); - warnCount++; - } + if (reservedSkillRoots.has(refName) || validSkills.has(refName)) continue; + console.warn(`WARN: ${file} - references skill directory skills/${refName}/ (not found locally)`); + warnCount++; } // Check agent name references in workflow diagrams (e.g., "planner -> tdd-guide") diff --git a/scripts/ci/validate-install-manifests.js b/scripts/ci/validate-install-manifests.js index f25aabc7..26c4c5ad 100644 --- a/scripts/ci/validate-install-manifests.js +++ b/scripts/ci/validate-install-manifests.js @@ -1,6 +1,8 @@ #!/usr/bin/env node /** * Validate selective-install manifests and profile/module relationships. + * Module paths are curated repo paths only. Generated/imported skill roots + * (~/.claude/skills/learned, etc.) are never in manifests. */ const fs = require('fs'); @@ -109,6 +111,7 @@ function validateInstallManifests() { const normalizedPath = normalizeRelativePath(relativePath); const absolutePath = path.join(REPO_ROOT, normalizedPath); + // All module paths must exist; no optional/generated paths in manifests if (!fs.existsSync(absolutePath)) { console.error( `ERROR: Module ${module.id} references missing path: ${normalizedPath}` diff --git a/scripts/ci/validate-skills.js b/scripts/ci/validate-skills.js index 220a25f1..7664b5d6 100644 --- a/scripts/ci/validate-skills.js +++ b/scripts/ci/validate-skills.js @@ -1,6 +1,8 @@ #!/usr/bin/env node /** - * Validate skill directories have SKILL.md with required structure + * Validate curated skill directories (skills/ in repo). + * Scope: curated only. Learned/imported/evolved roots are out of scope. + * If skills/ does not exist, exit 0 (no curated skills to validate). */ const fs = require('fs'); @@ -10,7 +12,7 @@ const SKILLS_DIR = path.join(__dirname, '../../skills'); function validateSkills() { if (!fs.existsSync(SKILLS_DIR)) { - console.log('No skills directory found, skipping validation'); + console.log('No curated skills directory (skills/), skipping'); process.exit(0); }