fix(kyberforge): unblock the scaffold and finish the #125 and ADR-0022 edits

Three related half-applied changes from #130, each leaving the corpus in a state its own
documentation contradicts.

Why:
- `assets/templates/SKILL.md` shipped `metadata:` fully commented out, and `new-skill.sh` only
  substitutes SKILL_NAME. Every scaffolded skill therefore lacked the `metadata.version` ADR-0022
  made mandatory and was blocked at first commit by the very hook this PR added. The commented
  example also read `"1.0"` — neither the `0.1.0` new-skill seed nor valid semver.
- `agent-audit/references/scope-project-user.md` still joined `disable-model-invocation` and
  `user-invocable` with a slash — #125's defect verbatim — while pointing the reader at the file
  this PR had just corrected to say the opposite.
- ADR-0022 required the "when present" bump conditional dropped and `metadata.version` moved into
  create.md's required list. It was dropped from SKILL.md but left in README.md, and the field was
  edited in place under a heading that still authorises removing it entirely.

Implementation notes:
- The template emits `metadata: version: "0.1.0"` live, captioned as required, with the optional
  keys left commented. `new-skill.bats` gains a case asserting a live key and three-part semver, so
  this cannot regress.
- `description-quality.md` now asserts only what the vendored Copilot research supports: two fields
  with opposite defaults, and the retired `infer` replaced by the pair rather than by either alone.
  The unsupported negative it previously stated as fact is gone.
- The `1.0.0` retrofit seed is stated in improve.md and retrofit.md, which the retrofit flow
  actually reads — create.md, where it lived, is unreachable from that path. The compression item
  moved out of the file-churn checklist, whose preamble excluded the wording-only change it covers.
- Executable git commands in these three skills now carry the ADR-0023 rtk prefix.

Refs: #125, #127
ADR: 0022, 0023
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EeH8SCbcrCAQrtymkNuhKP
This commit is contained in:
2026-09-09 05:15:23 +00:00
parent 175ea89c0a
commit 3811f5481b
11 changed files with 81 additions and 32 deletions

View File

@@ -7,7 +7,7 @@ description: >
directory -> skill-audit.
allowed-tools: Bash Read
metadata:
version: "1.0.0"
version: "1.0.1"
category: factory
source_keys:
- context7-websites-code-claude

View File

@@ -36,9 +36,12 @@ Read the frontmatter before judging a single word.
Its Claude Code counterpart has no equivalent field and stays model-invoked, so the two halves of
the pair carrying differently shaped descriptions is expected there rather than a
pair-consistency finding.
`user-invocable: false` does not belong in this bullet: it only blocks manual invocation and is
independent of `disable-model-invocation` — an agent can be `user-invocable: false` and still
model-routed, in which case the three-part shape below still applies. It carries no
`user-invocable: false` does not belong in this bullet. The two are separate fields with opposite
defaults — `disable-model-invocation` (default `false`) governs runtime auto-selection,
`user-invocable` (default `true`) governs manual invocation, and the retired `infer` field was
replaced by the pair rather than by either one. So `user-invocable: false` says nothing about
whether the agent is model-routed: judge that from `disable-model-invocation` alone, and where
that is absent the three-part shape below still applies. `user-invocable` carries no
description-quality contract of its own and is out of this file's scope entirely.
- **No such flag** — the agent is model-invoked and the rest of this file applies.

View File

@@ -41,8 +41,8 @@ those same sections, so any restatement is a copy that can disagree with the che
If it carries one, it still has to be kebab-case.
- `Use proactively` is meaningful in a CC description and steers the runtime to offer the agent
unprompted. In a Copilot description it does nothing; `KyberforgeCopilot.ProactivePhrase` flags
it. The Copilot equivalent is `disable-model-invocation` / `user-invocable`, which changes the
description contract entirely — see `references/description-quality.md`, Step 0.
it. The Copilot equivalent is `disable-model-invocation`, which changes the description contract
entirely — see `references/description-quality.md`, Step 0.
## Pair consistency

View File

@@ -6,7 +6,7 @@ description: >
Not read-only review -> `agent-audit`. Not skills -> `skill-author`.
allowed-tools: Bash Read Write Edit
metadata:
version: "1.0.0"
version: "1.0.1"
category: factory
source_keys:
- context7-websites-code-claude
@@ -31,7 +31,7 @@ metadata:
Signals: grill output, `agent-audit` findings, inline feedback, session context describing what went wrong. With none, ask: "No improvement signals found. Did you mean to create a new agent, or do you have feedback to apply?"
Read only the reference for the resolved flow. Capture `git log --oneline -1` before touching the filesystem; Step 4 needs it.
Read only the reference for the resolved flow. Capture `rtk git log --oneline -1` before touching the filesystem; Step 4 needs it.
## Step 2 — Scope
@@ -62,4 +62,4 @@ Invoke `agent-audit` on each file written and resolve every FAIL before reportin
At plugin/APM scope bump the resolved package's `apm.yml` `version` — **minor** on create, **patch** on improve — because consumers compare it to detect updates. Project and user scope have no manifest.
**Commit verification.** Once the audit is clean, run `git add` and `git commit` — do not stop at staging. Re-run `git log --oneline -1` and confirm the hash changed from Step 1's. A non-empty `git diff --stat` is not proof: staged-but-uncommitted work is part of no commit and is lost if the tree is cleaned up. Report done only once the hash has changed.
**Commit verification.** Once the audit is clean, run `rtk git add` and `rtk git commit` — do not stop at staging. Re-run `rtk git log --oneline -1` and confirm the hash changed from Step 1's. A non-empty `git diff --stat` is not proof: staged-but-uncommitted work is part of no commit and is lost if the tree is cleaned up. Report done only once the hash has changed.

View File

@@ -4,7 +4,7 @@ Author and refine skills conforming to the [agentskills.io](https://agentskills.
## What it does
Routes to one of two flows based on context: if no skill directory exists at the target path, it scaffolds the directory from annotated templates, fills in `SKILL.md` and supporting files, and validates the result. If an existing skill directory and improvement signals are both present, it groups those signals by root cause and applies targeted edits, then re-validates. In both flows, bumps the skill's `metadata.version` when present (minor for create, patch for improve).
Routes to one of two flows based on context: if no skill directory exists at the target path, it scaffolds the directory from annotated templates, fills in `SKILL.md` and supporting files, and validates the result. If an existing skill directory and improvement signals are both present, it groups those signals by root cause and applies targeted edits, then re-validates. In both flows, bumps the skill's `metadata.version` — minor for create, patch for improve — which every skill carries (ADR-0022).
`SKILL.md` itself carries only the dispatch table, the invocation-axis decision, the contract gates and the shared close; each flow lives in its own self-contained reference file, per ADR-0020.

View File

@@ -6,7 +6,7 @@ description: >
Not read-only review -> `skill-audit`. Not agent files -> `agent-author`.
allowed-tools: Bash Read Write Edit
metadata:
version: "1.0.0"
version: "1.0.1"
category: factory
source_keys:
- agentskills-home
@@ -34,7 +34,7 @@ metadata:
Signals: grill output, `/skill-audit` findings, inline feedback, eval results, session context describing what went wrong. With none, ask whether the user meant to create a new skill or has feedback to apply.
Read only the reference matching the resolved flow — each is self-contained. If the target sits inside a git worktree, capture `git log --oneline -1` before touching the filesystem; Step 4 needs it.
Read only the reference matching the resolved flow — each is self-contained. If the target sits inside a git worktree, capture `rtk git log --oneline -1` before touching the filesystem; Step 4 needs it.
## Step 2 — Invocation axis
@@ -59,4 +59,4 @@ Run `/skill-audit` on the resolved skill directory; resolve every FAIL before re
Bump `metadata.version`: the **minor** version on create (new skills start at `0.1.0`) and the **patch** version on improve.
**Commit verification.** Inside a git worktree: once the audit is clean, run `git add` and `git commit` — do not stop at staging. Re-run `git log --oneline -1` and confirm the hash changed from Step 1's. A non-empty `git diff --stat` is not proof: staged-but-uncommitted work is part of no commit and is silently lost if the tree is cleaned up. Report done only once the hash has changed. Outside a worktree (a skill under `~/.claude/skills/`, say) nothing is committable — report done on a clean audit, naming that as the reason.
**Commit verification.** Inside a git worktree: once the audit is clean, run `rtk git add` and `rtk git commit` — do not stop at staging. Re-run `rtk git log --oneline -1` and confirm the hash changed from Step 1's. A non-empty `git diff --stat` is not proof: staged-but-uncommitted work is part of no commit and is silently lost if the tree is cleaned up. Report done only once the hash has changed. Outside a worktree (a skill under `~/.claude/skills/`, say) nothing is committable — report done on a clean audit, naming that as the reason.

View File

@@ -45,14 +45,18 @@ description: >
# Optional. 1–500 characters. State tool requirements, runtime versions,
# and network access needs. Omit for skills with no special environment requirements.
# metadata:
metadata:
version: "0.1.0"
# author: your-name
# version: "1.0"
# category: general
# source_keys:
# - source-slug-one
# - source-slug-two
# Optional. Arbitrary key-value map. Common keys: author, version, category.
# `metadata.version` is REQUIRED on every skill (ADR-0022) and is enforced by the
# `skill-frontmatter` pre-commit hook. Three-component semver. A newly created
# skill starts at "0.1.0" — leave the seeded value as it is; "1.0.0" is the seed
# for a pre-existing skill retrofitted into the rule, not for a new one.
# The rest of the map is optional: author, category, source_keys.
# source_keys: populated when built from /research output. Lists slugs from references/sources.md.
# Also add source_keys to each references/*.md file that was informed by research.

View File

@@ -84,8 +84,10 @@ already covers the new skill. Use Read/Edit directly on `apm.yml`; this is not p
## Step 3 — Fill in SKILL.md
Open the new skill's `SKILL.md` (the path Step 1 printed) and replace every `FILL IN:`
placeholder. The scaffold template already carries the compliant frontmatter and body skeleton —
fill it rather than restructuring it.
placeholder. The scaffold template carries the ADR-0020 body skeleton and the two frontmatter
fields that cannot be left as placeholders — `name`, substituted by the script, and
`metadata.version`, seeded live at `"0.1.0"` — so fill the template in rather than restructuring
it.
**`name`** — already set by the scaffold script. Must exactly match the directory name. Format:
1–64 characters, lowercase letters, numbers and hyphens only; no leading, trailing or consecutive
@@ -96,15 +98,18 @@ against `references/contract.md`, which holds the three-part shape, the banned c
boundary-clause form and the length tiers. A hand-invoked skill (`SKILL.md` Step 2) takes one
plain sentence and `disable-model-invocation: true` instead.
**`metadata.version`** — required on every skill (ADR-0022), not a per-skill or per-plugin choice,
and enforced by the `skill-frontmatter` pre-commit hook. The scaffold seeds a new skill at
`"0.1.0"`; leave that value alone here and let `SKILL.md` Step 4 bump it. (`"1.0.0"` is the seed
for a pre-existing skill retrofitted into the rule, and never applies to a skill created here.)
**Optional frontmatter** — uncomment and fill in, or remove entirely:
- `license` — include when distributing the skill externally
- `compatibility` — include if the skill requires specific tools, runtimes, or network access
(max 500 characters)
- `metadata` — key-value map. `version` is **required** on every skill (ADR-0022), seeded at
`"1.0.0"` for a retrofitted skill with no prior version and at `"0.1.0"` for a newly created
skill; `author` and `category` stay optional; add `source_keys` now (Step 6) if research sources
are in context
- `metadata` — the rest of the map, all of it optional: `author` and `category`, plus `source_keys`
now (Step 6) if research sources are in context
- `allowed-tools` — space-separated pre-approved tools; reduces permission prompts (experimental —
support varies by client)
- `disable-model-invocation` — hand-invoked skills only

View File

@@ -85,6 +85,10 @@ improvise the cuts — four dry runs invented six to ten different answers to th
If a signal points to a script or reference file, edit that file directly rather than adding a
workaround in SKILL.md.
**A skill carrying no `metadata.version` is seeded at `"1.0.0"`, not bumped** — `SKILL.md` Step 4's
patch bump presumes a version to bump, and ADR-0022 reserves `"0.1.0"` for a newly created skill.
`references/retrofit.md` carries the reasoning.
**Check for regressions before handing back.** `SKILL.md` Step 4 tells you to resolve every FAIL,
which says nothing about a check that passed *before* these edits and no longer does. Compare the
closing audit against the skill's pre-edit state — a PASS that has become a SUGGESTION, or a

View File

@@ -105,16 +105,37 @@ them for you. After every retrofit that adds, removes or renames a file:
zero.
- [ ] Re-run `/skill-audit` and confirm its `### Provenance` dimension does not report the new
file as missing `source_keys`.
- [ ] **Compression must not add authority the source text didn't have.** The bullet above is
about a `sources.md` entry going *stale* — Contributing files left uncited after content
moves. This is a distinct failure: a compression or rewrite pass that upgrades an honest
hedge in a Description into an unsupported confident claim, without the underlying source
having changed at all — "no forge-specific content drawn directly from it beyond that"
quietly becoming "Grounds Step 2's dispatch table." Nothing in `/skill-audit`'s structural
checks catches this; a bash script can verify an entry is internally consistent, never
whether the claim is *true*. If a retrofit strengthens or otherwise changes the wording of a
provenance claim, re-read the upstream research doc first and confirm the stronger wording
is actually still true before committing it.
## Compression must not add authority the source text didn't have
This one is **not** part of the checklist above, and deliberately so: it fires on a wording change
with no file change at all, so a retrofit that adds and removes nothing still owes it.
The `sources.md` bullet above is about an entry going *stale* — Contributing files left uncited
after content moves. This is a distinct failure: a compression or rewrite pass that upgrades an
honest hedge in a Description into an unsupported confident claim, without the underlying source
having changed at all — "no forge-specific content drawn directly from it beyond that" quietly
becoming "Grounds Step 2's dispatch table."
`/skill-audit`'s provenance script does now notice this class: it diffs each slug's `Description`
and `Contributing files` text against a base ref and raises an **INFO** when the wording changed.
That is a prompt, not a verdict — it reports only *that* the claim moved, never whether the new
claim is true, because a bash script can verify an entry is internally consistent and nothing more.
Answering it is this flow's job: if a retrofit strengthens or otherwise changes the wording of a
provenance claim, re-read the upstream research doc first and confirm the stronger wording is
actually still true before committing it.
## Versioning a retrofitted skill
`SKILL.md` Step 4 says to bump the **patch** version on improve, which presumes there is a version
to bump. A pre-ADR-0020 skill often carries none — `metadata.version` only became mandatory under
ADR-0022, and this flow is exactly where those skills surface.
A skill with no `metadata.version` is **seeded at `"1.0.0"`, not bumped**. `"0.1.0"` is reserved
for a skill created new by the create flow: it means "created and never yet revised", which
understates a skill that has been through retrofit and audit passes without tracking a version.
Add the field in this retrofit — the `skill-frontmatter` pre-commit hook blocks the commit without
it.
## Worked example — a description retrofit

View File

@@ -156,6 +156,18 @@ EOF
assert_success
}
@test "scaffold emits a live metadata.version seeded at 0.1.0 (ADR-0022)" {
# The scaffold must clear .pre-commit-config.yaml's `skill-frontmatter` hook
# on its first commit: a commented-out metadata block ships a skill with no
# version and is blocked. Assert the field is live, not a comment.
bash "$SCRIPT" my-tool "$DEST"
run grep -E '^metadata:$' "$DEST/my-tool/SKILL.md"
assert_success
run grep -E '^ version: "[0-9]+\.[0-9]+\.[0-9]+"$' "$DEST/my-tool/SKILL.md"
assert_success
assert_output --partial '0.1.0'
}
@test "walk-up skips a type-less apm.yml (marketplace-only) and finds a real package root further up" {
mkdir -p "$DEST/mid/sub"
cat > "$DEST/apm.yml" <<'EOF'