odin-skill-manager
A skill's provenance is not a fact about its content. It is a fact about what may be done to it —
and the only place that fact can live is somewhere a gate can read.
What this owns, and what it does not
Four skills already own parts of skill work. This one owns what none of them does: where a skill
came from, whether it may be overwritten, whether it is published, and how far it has drifted.
| The task |
Its owner |
| Writing a new skill's body, frontmatter, references |
skill-creator, then writing-skills |
| Whether a skill's own instructions are being followed |
skill-comply |
| Finding a skill that already does the thing |
find-skills, skill-repo |
| Whether a new thing duplicates an existing one |
consistency |
| Distilling recurring skill patterns into rules |
rules-distill |
| Classification, mirror membership, packaging, freshness, refresh, publication |
here |
Creating a skill is therefore always two steps: skill-creator writes it, and this registers it.
A skill that exists but is not registered is invisible to the gate that decides what a refresh may
destroy.
The four classes
The class is derived on every run by .claude/scripts/lib/skill-provenance.sh from four inputs —
the vend map, the FROZEN list, an UPSTREAM.md, and mirror membership.
| Class |
Means |
A refresh may overwrite it |
vendored |
third-party, unmodified here |
yes — that is the point |
forked |
third-party, modified here |
no — data loss with no upstream copy |
authored |
written here, no upstream exists |
no |
frozen |
vendored, but upstream is gone or unusable |
no — there is nothing to refresh from |
A fifth answer exists and is not a class: undetermined, rc 3. Nothing claims the skill and
nothing declares it. It is never folded into authored, because a skill installed by a provisioner
looks identical on disk to one written here, and guessing either way protects or exposes the wrong
thing. sp_protected_set refuses entirely while any skill is undetermined — a partial protect list
is the shape that overwrites exactly the skill nobody could classify.
Precedence, and the one line of it that matters: fork evidence outranks the vend map, because a
forked skill is usually still in the map. rules-distill is vended from ECC and forked here.
The commands
bash .claude/scripts/skill-provenance-check.sh # every skill has a class
bash .claude/scripts/skill-provenance-check.sh --membership # every owned skill is published
bash .claude/scripts/skill-freshness.sh --quick # did any upstream move (17 ls-remote)
bash .claude/scripts/skill-freshness.sh --deep # which skills differ, and by how much
bash .claude/scripts/skill-freshness.sh --deep --only <name> # one skill
bash .claude/scripts/vendor-skills.sh --print-map # the upstream map, TSV, no network
bash .claude/scripts/vendor-skills.sh --refresh # replace vendored skills only
bash projects/Odin-Skills/scripts/sync-from-odin.sh --check # mirror drift
bash projects/Odin-Skills/scripts/validate-skills.sh # the mirror's own gate
Exit codes are the interface. skill-freshness separates 1 (a vendored skill is behind) from
3 (an upstream could not be read) on purpose: "it moved" and "nobody could tell" are different
sentences, and only one of them is a finding about a skill.
The five operations
1. A new skill
skill-creator writes it. This skill does not author bodies.
- Register it:
skill-provenance-check.sh must pass. An Odin-authored skill classifies as
authored only once it is a mirror member — until then it is undetermined and needs a
declared row, which is a debt, not a record.
- Publish it: references/membership-runbook.md.
- Route it. A skill nothing names is a skill nothing fires —
skill-registry-coverage.sh and
skill-routing-check.sh hold that.
2. Forking a vendored skill
Full procedure: references/fork-runbook.md. The short form — the
edit is the last step, never the first:
- Decide fork vs freeze. A freeze says "stop re-pulling this"; a fork says "Odin maintains this
now and republishes it". Publishing someone else's skill has a licensing story; freezing does not.
- Write
UPSTREAM.md before editing the body: upstream, the sha forked from, and what a porter
must read first.
- Verify the class flipped:
sp_class must report forked, and the protect list must contain it.
- Publish, with the upstream
LICENSE beside the UPSTREAM.md — or, where upstream published
none, the literal declaration no LICENSE file accompanied plus a NOTICE.
3. Refreshing vendored skills
references/refresh-runbook.md. Never hand-edit a vendored body:
the next refresh reverts it, silently, and the finding closes while the cost comes back. Two edits
are applied mechanically by vend() for exactly that reason — the frontmatter name: rewrite, and
stripping disable-model-invocation: true.
4. Publishing to the mirror
projects/Odin-Skills/ is a one-way publication of .claude/skills/; the harness is
authoritative. A change made in the mirror is destroyed by the next sync. Membership is a rule, not
a list (odin-skills:ADR-0003), and --membership is its predicate.
5. Retiring a skill
Deletion is not enough. Remove it from the vend list with the reason inline, or the next run
re-vendors it — grill-me was re-created on every run for weeks after being retired. Then: mirror
member removed, registries updated, CHANGELOG.md records it.
Red flags
| Thought |
Reality |
| "I'll just patch the vendored skill" |
The next refresh reverts it. Fork it, or freeze it, or fix it upstream. |
| "It's ours because it's in the mirror" |
Backwards. It is in the mirror because it is ours. That circularity is what harness:RM-0473 removed. |
| "The refresh was clean, nothing broke" |
Run skill-invocability-check.sh. A refresh reintroduced disable-model-invocation on four routed skills in one command. |
| "No diff means up to date" |
Only if the comparison used the upstream's own directory name. Guessing by target name misses every renamed skill. |
| "The freshness doc says…" |
A document cannot notice that upstream moved. Run --deep. |
| "I'll add the provenance row later" |
The row is what stops a refresh destroying it. Later is after the refresh. |
| "Publishing is just a copy" |
It is a licensing act. validate-skills.sh check 6 refuses a fork with no licence artefact, and it is right to. |
Reference
| Need |
Where |
| What each class licenses, and how it is derived |
references/classification.md |
| Refreshing without destroying local work |
references/refresh-runbook.md |
| Adding a member to the published mirror |
references/membership-runbook.md |
| Turning a vendored skill into a fork |
references/fork-runbook.md |
| Declared rows, and why most of them are debts |
.claude/docs/skill-provenance.tsv |
| The rule half |
.claude/rules/skills/lifecycle.md |
1---2name: odin-skill-manager3description: Use when a skill enters, changes, or leaves this harness — creating one and registering it, forking a vendored skill, refreshing vendored skills from upstream, publishing an owned skill to the Odin-Skills mirror, retiring one, or answering "is this skill ours". Also on "vendored skill", "upstream skill", "fork a skill", "refresh skills", "skill provenance", "mirror membership", "publish a skill", "is this skill up to date". Owns classification, membership, packaging and freshness; routes authoring to skill-creator and writing-skills.4---56# odin-skill-manager78> A skill's provenance is not a fact about its content. It is a fact about what may be done to it —9> and the only place that fact can live is somewhere a gate can read.1011## What this owns, and what it does not1213Four skills already own parts of skill work. This one owns what none of them does: **where a skill14came from, whether it may be overwritten, whether it is published, and how far it has drifted.**1516| The task | Its owner |17|---|---|18| Writing a new skill's body, frontmatter, references | `skill-creator`, then `writing-skills` |19| Whether a skill's own instructions are being followed | `skill-comply` |20| Finding a skill that already does the thing | `find-skills`, `skill-repo` |21| Whether a new thing duplicates an existing one | `consistency` |22| Distilling recurring skill patterns into rules | `rules-distill` |23| **Classification, mirror membership, packaging, freshness, refresh, publication** | **here** |2425Creating a skill is therefore always two steps: `skill-creator` writes it, and this registers it.26A skill that exists but is not registered is invisible to the gate that decides what a refresh may27destroy.2829## The four classes3031The class is derived on every run by `.claude/scripts/lib/skill-provenance.sh` from four inputs —32the vend map, the `FROZEN` list, an `UPSTREAM.md`, and mirror membership.3334| Class | Means | A refresh may overwrite it |35|---|---|---|36| `vendored` | third-party, unmodified here | **yes** — that is the point |37| `forked` | third-party, modified here | no — data loss with no upstream copy |38| `authored` | written here, no upstream exists | no |39| `frozen` | vendored, but upstream is gone or unusable | no — there is nothing to refresh from |4041A fifth answer exists and is not a class: **`undetermined`**, rc 3. Nothing claims the skill and42nothing declares it. It is never folded into `authored`, because a skill installed by a provisioner43looks identical on disk to one written here, and guessing either way protects or exposes the wrong44thing. `sp_protected_set` refuses entirely while any skill is undetermined — a partial protect list45is the shape that overwrites exactly the skill nobody could classify.4647Precedence, and the one line of it that matters: **fork evidence outranks the vend map**, because a48forked skill is usually still in the map. `rules-distill` is vended from ECC *and* forked here.4950## The commands5152```sh53bash .claude/scripts/skill-provenance-check.sh # every skill has a class54bash .claude/scripts/skill-provenance-check.sh --membership # every owned skill is published55bash .claude/scripts/skill-freshness.sh --quick # did any upstream move (17 ls-remote)56bash .claude/scripts/skill-freshness.sh --deep # which skills differ, and by how much57bash .claude/scripts/skill-freshness.sh --deep --only <name> # one skill58bash .claude/scripts/vendor-skills.sh --print-map # the upstream map, TSV, no network59bash .claude/scripts/vendor-skills.sh --refresh # replace vendored skills only60bash projects/Odin-Skills/scripts/sync-from-odin.sh --check # mirror drift61bash projects/Odin-Skills/scripts/validate-skills.sh # the mirror's own gate62```6364Exit codes are the interface. `skill-freshness` separates **1** (a vendored skill is behind) from65**3** (an upstream could not be read) on purpose: "it moved" and "nobody could tell" are different66sentences, and only one of them is a finding about a skill.6768## The five operations6970### 1. A new skill71721. `skill-creator` writes it. This skill does not author bodies.732. Register it: `skill-provenance-check.sh` must pass. An Odin-authored skill classifies as74 `authored` **only once it is a mirror member** — until then it is `undetermined` and needs a75 declared row, which is a debt, not a record.763. Publish it: [references/membership-runbook.md](references/membership-runbook.md).774. Route it. A skill nothing names is a skill nothing fires — `skill-registry-coverage.sh` and78 `skill-routing-check.sh` hold that.7980### 2. Forking a vendored skill8182Full procedure: [references/fork-runbook.md](references/fork-runbook.md). The short form — the83edit is the *last* step, never the first:84851. Decide fork vs freeze. A freeze says "stop re-pulling this"; a fork says "Odin maintains this86 now and republishes it". Publishing someone else's skill has a licensing story; freezing does not.872. Write `UPSTREAM.md` **before** editing the body: upstream, the sha forked from, and what a porter88 must read first.893. Verify the class flipped: `sp_class` must report `forked`, and the protect list must contain it.904. Publish, with the upstream `LICENSE` beside the `UPSTREAM.md` — or, where upstream published91 none, the literal declaration `no LICENSE file accompanied` plus a `NOTICE`.9293### 3. Refreshing vendored skills9495[references/refresh-runbook.md](references/refresh-runbook.md). Never hand-edit a vendored body:96the next refresh reverts it, silently, and the finding closes while the cost comes back. Two edits97are applied mechanically by `vend()` for exactly that reason — the frontmatter `name:` rewrite, and98stripping `disable-model-invocation: true`.99100### 4. Publishing to the mirror101102`projects/Odin-Skills/` is a one-way publication of `.claude/skills/`; the harness is103authoritative. A change made in the mirror is destroyed by the next sync. Membership is a rule, not104a list (`odin-skills:ADR-0003`), and `--membership` is its predicate.105106### 5. Retiring a skill107108Deletion is not enough. Remove it from the vend list **with the reason inline**, or the next run109re-vendors it — `grill-me` was re-created on every run for weeks after being retired. Then: mirror110member removed, registries updated, `CHANGELOG.md` records it.111112## Red flags113114| Thought | Reality |115|---|---|116| "I'll just patch the vendored skill" | The next refresh reverts it. Fork it, or freeze it, or fix it upstream. |117| "It's ours because it's in the mirror" | Backwards. It is in the mirror because it is ours. That circularity is what harness:RM-0473 removed. |118| "The refresh was clean, nothing broke" | Run `skill-invocability-check.sh`. A refresh reintroduced `disable-model-invocation` on four routed skills in one command. |119| "No diff means up to date" | Only if the comparison used the upstream's own directory name. Guessing by target name misses every renamed skill. |120| "The freshness doc says…" | A document cannot notice that upstream moved. Run `--deep`. |121| "I'll add the provenance row later" | The row is what stops a refresh destroying it. Later is after the refresh. |122| "Publishing is just a copy" | It is a licensing act. `validate-skills.sh` check 6 refuses a fork with no licence artefact, and it is right to. |123124## Reference125126| Need | Where |127|---|---|128| What each class licenses, and how it is derived | [references/classification.md](references/classification.md) |129| Refreshing without destroying local work | [references/refresh-runbook.md](references/refresh-runbook.md) |130| Adding a member to the published mirror | [references/membership-runbook.md](references/membership-runbook.md) |131| Turning a vendored skill into a fork | [references/fork-runbook.md](references/fork-runbook.md) |132| Declared rows, and why most of them are debts | `.claude/docs/skill-provenance.tsv` |133| The rule half | `.claude/rules/skills/lifecycle.md` |