Immutable. This exact content is served forever at /api/v1/blob/75cf176b93f3bc3f.
---
name: skill-provenance
description: >
Version, validate, package, verify, recover, and hand off Agent Skill
bundles across local folders, registries, platform uploads, and agent
sessions. Use for MANIFEST.yaml, CHANGELOG.md, bundle hashes, stale evals,
frontmatter portability, derived skill packages, or version identity that
must survive filename changes and cross-platform movement. Do not use for
ordinary Git version control that does not involve an Agent Skill bundle.
Compatible with the agentskills.io open standard.
license: MIT
metadata:
skill_bundle: skill-provenance
file_role: skill
version: 25
version_date: 2026-08-28
previous_version: 24
change_summary: >
Added standalone verification and bootstrap routing for users who have
not installed the plugin while keeping validate.sh as the sole parser.
author: PAICE.work PBC (paice.work)
source: https://github.com/snapsynapse/skill-provenance
---
# Skill Provenance
## The Problem This Solves
Skill projects move between sessions, surfaces (Chat, IDE, CLI, Cowork),
platforms (Claude, Gemini CLI, Codex, Copilot), and local storage
(Obsidian, working directories, git repos). Version identity gets lost
when it lives only in filenames. A file renamed from `SKILL_v4.md` to
`SKILL_v5.md` with no internal record of what changed creates ambiguity.
This skill establishes three conventions that prevent that:
1. Version identity lives inside files when their format allows it, and
always in the manifest.
2. A recent changelog travels with the skill bundle, while longer history
can live in the source repo.
3. A manifest lists all files in the bundle so any session can verify completeness.
## What Gets Versioned
A skill bundle is a SKILL.md plus all associated files. Typical contents:
- SKILL.md (the skill definition)
- evals.json (evaluation suite)
- Generation scripts (e.g., generate.js, generate.py)
- Output artifacts (.docx, .pdf) produced by evals or real use
- Handoff notes
- Source material provided by the user (tracked but not versioned)
The skill itself (SKILL.md) and evals are the primary versioned artifacts.
Scripts and outputs are tracked by the manifest but version with the bundle
rather than independently. Handoff notes are optional convenience artifacts.
## Internal Version Header
Files that can safely carry YAML frontmatter begin with a YAML frontmatter
block (or extend an existing one) containing these fields:
```yaml
---
skill_bundle: my-skill # bundle name, stable across versions
file_role: skill # skill | evals | script | output | handoff
version: 5 # integer, monotonically increasing
version_date: 2026-02-10 # date of this version
previous_version: 4 # null for v1
change_summary: >
Rewrote Phase 5 layout rules. Removed per-section page breaks.
Added content flow check. Added validation checklist as standalone final page.
---
```
### Rules
**version** is an integer for per-file tracking. It counts revisions to
that specific file within the bundle. The bundle-level version
(`bundle_version` in MANIFEST.yaml) uses semver.
**change_summary** is required for every version after v1. One to three
sentences. It must describe what changed, not just that something changed.
**previous_version** creates a chain. Any session can trace the lineage.
**file_role** values:
- `skill` — the SKILL.md itself
- `evals` — the evals.json file
- `script` — generation scripts, utility scripts
- `output` — rendered artifacts (.docx, .pdf)
- `handoff` — session handoff notes
- `source` — user-provided source material (tracked, not versioned)
- `reference` — documentation in references/ loaded on demand
- `asset` — templates, images, fonts in assets/ used in output
- `agents` — platform UI metadata (e.g., Codex's agents/openai.yaml)
For files that cannot safely carry YAML frontmatter (binary files and
strict-format files such as `.json` or executable `.sh`), the manifest
tracks their version and its `version` field is authoritative.
**SKILL.md frontmatter constraint:** The Agent Skills open standard
(agentskills.io) requires `name` and `description`. Different platforms
enforce different rules about additional fields:
| Platform | Allowed SKILL.md frontmatter |
|---|---|
| **agentskills.io spec** | `name`, `description`, `license`, `metadata`, `compatibility`, `allowed-tools` |
| **Claude Chat / Settings UI** | Same as spec. Claude's settings importer rejects unrecognized fields. |
| **Claude Code** | Spec fields plus extensions: `disable-model-invocation`, `user-invocable`, `context`, `agent`, `model`, `hooks`, `argument-hint`. These are Claude Code features, not part of the base spec. |
| **Claude API** | Skills uploaded via `/v1/skills`. Validates `name` and `description`. Supports `metadata`. |
| **Gemini CLI (Google)** | `name` and `description` only. Extra fields not officially supported. |
| **Codex (OpenAI)** | `name` and `description` only. Extra fields rejected. |
| **GitHub Copilot / VS Code** | Follows agentskills.io spec. |
| **Cursor, Roo Code, Junie, others** | Follows agentskills.io spec. See agentskills.io for the full adopter list (30+). |
For maximum portability, keep SKILL.md frontmatter to `name` and
`description` only. If the canonical bundle needs attribution or visible
SKILL.md metadata, use the spec's `metadata` field there and generate a
derived minimal copy for strict platforms:
```yaml
---
name: my-skill
description: What the skill does.
metadata:
skill_bundle: my-skill
file_role: skill
version: 3
version_date: 2026-02-10
previous_version: 2
change_summary: >
Added Phase 6 validation step.
---
```
If targeting Codex or other strict platforms directly, omit `metadata`
from SKILL.md entirely. The manifest tracks SKILL.md's version either
way, so no version information is lost.
**Note on spec support:** The agentskills.io spec formally supports
`metadata` as an arbitrary key-value map, with `version` shown as an
example use. This means the `metadata.version` approach is now
spec-blessed, not a Claude-only extension. However, the spec's version
is a static label — it does not address staleness tracking, changelogs,
or bundle integrity. Prefer manifest-based tracking as the default and
use `metadata` only when you need version info visible in the file
itself.
## Manifest
The manifest is a YAML file named `MANIFEST.yaml` at the root of the skill
bundle directory — the same level as `SKILL.md`. When the bundle is
packaged as a `.skill` ZIP, the manifest lives inside the ZIP. It is the
single source of truth for what the bundle contains.
```yaml
bundle: my-skill
bundle_version: 5.1.0
bundle_date: 2026-02-10
description: >
Skill for generating professional documents from source material
and user briefs. Handles research, structuring, and formatting.
compatibility:
designed_for:
surfaces:
- chat
- cli
- ide
capabilities:
- minimal SKILL.md frontmatter
- local filesystem access
- optional git workflow
tested_on:
- platform: Anthropic Claude
model: Claude Opus 4.6
surface: Chat
status: pass
date: 2026-02-10
- platform: Anthropic Claude
model: Claude Sonnet 4.5
surface: Chat
status: partial
date: 2026-02-09
notes: Misses staleness detection on complex bundles
spec_version: agentskills.io/1.0
frontmatter_mode: minimal
# minimal = name + description only (Codex, Gemini CLI, max portability)
# metadata = includes metadata block (any platform supporting the spec's metadata field)
dependencies: []
# List skill names this bundle depends on. Omit or leave empty if none.
validated_against:
# Optional attestation records, each bound to the exact bundle_version it
# validated. Informational only: validate.sh reports them, never gates on them.
- bundle_version: 5.1.0
harness: Anthropic Claude Code
model: Claude Opus 4.6
date: 2026-02-10
result: pass
deployments:
api:
version: 1759178010641129
workspace: docs-prod
claude:
scope: user
perplexity:
package_format: zip
origin:
source_kind: git-repo
source: owner/repo
resolved_ref: main@abc123
selected_source_path: skills/my-skill
ignored_duplicate_source_paths:
- examples/agent-skills/my-skill
- .well-known/agent-skills/my-skill
derived_from_bundle_version: 5.1.0
target_surface: codex
files:
- path: SKILL.md
role: skill
version: 5
hash: sha256:abc123...
note: Canonical skill definition
- path: evals.json
role: evals
version: 3
hash: sha256:def456...
note: 7 evals including real-content synthesis
- path: scripts/generate.js
role: script
version: 4
hash: sha256:ghi789...
note: Generation script for eval 3
- path: outputs/eval3-output.pdf
role: output
version: 4
hash: sha256:jkl012...
note: Rendered eval 3 output, 10 pages, validated
- path: sources/article-1.md
role: source
version: null
hash: sha256:pqr678...
note: Source article 1 (published)
```
### Rules
**bundle_version** uses semver (MAJOR.MINOR.PATCH). Bump MAJOR for
breaking changes to the skill's model or interface, MINOR for new
features or capabilities, PATCH for fixes and documentation updates.
Per-file `version` fields remain integers — they are revision counters,
not release identifiers.
**hash** is sha256 of the file contents. This is how a new session verifies
that the file it received matches what the manifest claims. Compute on save,
verify on load. Every tracked file must use either a complete lowercase
`sha256:` value or an explicit `hash: null` opt-out. A missing or malformed
hash is invalid. `validate.sh --update` repairs missing or malformed hashes
when the corresponding file is present; it preserves explicit null opt-outs.
**deployments** is optional. Use it to record deployed or installed copies
of the same bundle when you want traceability across surfaces. Keep
`bundle_version` as the author-side semver source of truth. Platform-native
versions (for example API timestamps) stay in `deployments`, not in
`bundle_version`.
**validated_against** is optional. Each entry attests that a specific
`bundle_version` was validated on a specific harness and model, with a
`result` of pass, partial, or fail. This is a different claim from
`compatibility.tested_on` (design-time compatibility, not bound to a
release) and a different concern from `hash` (integrity). Integrity gates:
a hash mismatch fails validation. Attestation informs: validate.sh reports
entries matching the current `bundle_version` and flags staleness when none
match, but never changes its exit code over attestation. The same pinned
bytes can behave differently as harnesses and models move, so a stale
attestation means re-validate, not reject.
Each record requires `bundle_version`, `harness`, an ISO `YYYY-MM-DD` date,
and `result: pass`, `partial`, or `fail`. Malformed records are reported but
do not count as matching evidence.
**origin** is optional. Use it in derived strict-platform copies, registry
packages, settings exports, or installed copies when the selected source
path matters. It records which source path crossed the boundary and which
lookalike duplicate paths were intentionally ignored. Do not use it as a
package-manager lockfile, installer state machine, or trust anchor.
**version: null** for source files. They are tracked for completeness but
not versioned by this system.
**File paths use a constrained, fail-closed grammar.** The inventory must
use a top-level `files:` line, entries formatted exactly as
` - path: <unquoted-relative-path>`, and hash fields formatted as
` hash: <value>`. Paths must be unique, normalized, and relative to the
bundle root. Reject absolute paths, `.` or `..` components, empty path
components, trailing slashes, backslashes, surrounding whitespace, inline
comments, YAML quotes, anchors, aliases, or tags. Symlinks in any path
component are invalid rather than followed. These rules intentionally define a small
line-oriented YAML subset so the zero-dependency parser does not silently
disagree with a general YAML implementation.
**MANIFEST.yaml is not listed in `files`.** Self-hashing is recursive. Treat
the manifest as the bundle's control file and verify it via git, transport
checksums, or the surrounding package when needed.
## The .skill Package Format
Treat `.skill` files as ZIP archives whose top-level directory is the skill
name. Include every manifest-listed file in an authored package. A reduced
install package is valid only when its own derived manifest exactly describes
its reduced inventory. Read [references/packaging-and-changelog.md](references/packaging-and-changelog.md)
when creating archives, derived packages, or changelog entries.
## Changelog
Keep `CHANGELOG.md` beside `SKILL.md` and `MANIFEST.yaml`, newest entry first.
Name every changed file, describe intent, and call out dependent files left
stale. A source repository may preserve older append-only history outside the
portable bundle. See [references/packaging-and-changelog.md](references/packaging-and-changelog.md)
for the format and trimming rules.
## Session Protocol
### Validating only
When the user asks only to validate a bundle, run the hash and inventory
check without doing the full open-session review or close-session update:
1. Read `MANIFEST.yaml` and verify all listed files are present.
2. Run `validate.sh` when available, or compute SHA-256 hashes for listed
files and compare them against the manifest.
When the plugin and local helper are both absent, read
[references/standalone-verification.md](references/standalone-verification.md)
and use its pinned standalone wrapper rather than recreating the parser.
3. Report checked files, missing files, hash mismatches, skipped files,
and pass/fail status.
Treat only `hash: null` as an intentional skip; missing, malformed, or
duplicate hash fields are manifest errors.
4. Identify whether the copy appears to be a canonical source bundle,
strict-platform install copy, registry package, or ambiguous copy based
on its own manifest and local contents.
5. Do not update hashes, bump versions, edit the changelog, create a
handoff note, or run the close protocol unless the user explicitly asks
for a mutation.
This is an integrity check, not a trust anchor.
### Packaging derived copies
Before building a strict-platform install copy, registry package, or
settings ZIP, verify the canonical source bundle first. The included
`package.sh` helper runs `validate.sh` against the canonical bundle at each
derived-package boundary and must stop if the manifest reports invalid
structure, unsafe paths, symlinks, duplicate paths, missing files, or hash
mismatches. `validate.sh` remains the single parser and policy authority so
validation and packaging cannot drift into separate grammars.
Do not treat generated strict-loader, ClawHub, or `.skill` outputs as the
canonical source bundle. They are derived artifacts whose own manifests
must describe exactly the files they contain.
When a derived copy is selected from a repo, registry package, archive, or
platform export that contains multiple lookalike skills, preserve that
selection in optional `origin` metadata if the user or installer provides
the source facts. Record `selected_source_path` and any intentionally
ignored duplicate paths, but leave consumer lockfile and update semantics
to the package manager.
### Opening a session
When a skill bundle is loaded into a new session:
1. Read `MANIFEST.yaml` first.
2. Verify all listed files are present. Report any missing files.
3. Reject invalid inventory structure, unsafe or duplicate paths, and
symlinks in any path component before reading or hashing a listed file.
4. For files with hashes, verify hashes match. Flag mismatches. In
local environments, users can run `validate.sh` before uploading
for reliable hash verification without LLM computation.
5. Read `CHANGELOG.md` to understand recent changes.
6. Check for staleness using hash drift, changelog dependency notes,
conflicting internal metadata, `validated_against`, and deployment
records. Never order per-file revision integers against bundle semver;
they are separate version domains.
7. If `MANIFEST.yaml` is missing, treat the bundle as unversioned. Offer
to create one by inventorying the files and asking the user for version
context.
### Saving / closing a session
When work is complete and files are being delivered:
1. Update internal version headers for changed files that use them.
2. Update `MANIFEST.yaml` with new versions and hashes for every changed
versioned file, including manifest-only files. If the user deployed or
reinstalled the skill this session, update any relevant `deployments`
metadata too.
3. Add a new top entry to `CHANGELOG.md`.
4. If any versioned file was changed but another dependent file was not
updated (e.g., SKILL.md changed but evals.json was not updated), note
the staleness explicitly in the changelog entry.
5. Deliver the full bundle to the user, or at minimum the changed files
plus the updated MANIFEST.yaml and CHANGELOG.md.
6. If the user indicates the bundle is destined for a git repo, provide
a ready-to-use commit message derived from the changelog entry. Format:
```
skill-name MAJOR.MINOR.PATCH: one-line summary
- file1.md: what changed
- file2.json: what changed
- Stale: file3.js (not updated this session)
```
Return the message inline by default. Only write a transient
`git_commit.txt` file if the user explicitly asks for a file or if the
environment makes file output materially more convenient.
### Handoff between sessions
A handoff note is a snapshot of project state for the next session. It
should include:
- Current bundle version
- What was accomplished this session
- What is stale and needs attention
- What the next session should do first
- Any decisions made that are not yet reflected in the files
- Per-file change summaries: for each file modified this session, a
brief description of what changed (section added, field removed,
logic rewritten, etc.). This is more granular than the changelog
entry and helps the next session verify the work without re-reading
every file.
Create a handoff note only when crossing a non-persistent boundary or when
the user explicitly asks for one. In filesystem-native environments with a
current manifest, changelog, and git history, it is usually unnecessary.
When created, it replaces the previous handoff note; previous handoffs live
in changelog history.
### Conflict resolution
When a session finds version conflicts (e.g., a file claims v5 but the
manifest says v4, or two files claim different bundle versions):
1. Present the conflict to the user with the specific discrepancy.
2. Show what each version claims via its change_summary.
3. Default recommendation: trust the most recent version_date.
4. Always ask the user for explicit confirmation before proceeding.
Never silently resolve a version conflict. The whole point of this system
is to make conflicts visible.
## Cross-Surface and Cross-Platform Considerations
Keep the canonical source authoritative. Treat strict-loader copies, registry
packages, settings archives, and platform deployments as derived state. Never
turn package metadata or assistant-facing files into authority. Read
[references/platforms-and-trust.md](references/platforms-and-trust.md) when
choosing a target surface, recording origin or deployment state, or evaluating
how this skill relates to registries and package managers.
## File Naming
Versioned files use stable names without version numbers:
- `SKILL.md` (not `SKILL_v5.md`)
- `evals.json` (not `evals_v3.json`)
- `generate.js` (not `generate-v4.js`)
The version lives inside the file (via the header) and in the manifest,
not in the filename. Version-numbered filenames are how we got into
trouble in the first place.
Exception: if a user's local storage requires version-in-filename for
their workflow, the manifest is the tiebreaker for which version is
canonical. Internal version identity must still match.
## Bootstrap
For an unversioned bundle, inventory files yourself, establish an initial
version from available history or user direction, add safe internal headers,
create `MANIFEST.yaml` with hashes, create the first changelog entry, validate,
and deliver the complete bundle. This is a one-time operation per bundle.
When the skill is not installed, use the portable prompt in
[references/standalone-verification.md](references/standalone-verification.md)
and keep the same manifest, changelog, validation, and authority boundaries.
## Origin
Developed for [PAICE.work](https://paice.work/) PBC. Canonical source:
https://github.com/snapsynapse/skill-provenance