refactor(skills): retrofit the corpus to the ADR-0020 context contract (#129)

Retrofits all 39 skills to ADR-0020's description/body context contract, then fixes what six rounds of independent review found in that retrofit — including four ways the hot gate itself failed open.

Closes #99, #107, #108, #110, #111, #114, #115, #120.

## The retrofit (waves 1-5)

| | Start | Now |
|---|---|---|
| Description FAILs (>400 chars) | 26 | **0** |
| Body FAILs (>900 words, body-only) | 9 | **0** |
| Dangling routing targets | 2 | **0** |
| `Kyberforge.CompositionNote` | 10 | **0** |
| Preload tax | 21,005 chars | **~10,500** |

Under the 12,000-char success criterion. Per-wave detail is on #99.

## The review fixes

**The gate failed open four ways, three of them found after the retrofit shipped.** An unrecognised follower token made a dangling target vanish. A skill directory with no `SKILL.md` resolved as a valid target, so a commit could be green locally and red in a fresh clone — three existing fixtures were relying on that, one of which made the install-leak A/B pass vacuously. Then the free-standing `/name` sweep turned out to be gated on the sentence carrying a boundary marker, so route notation in any other sentence was invisible — not an ERROR, not a SUGGESTION, not an INFO — which left the documented "`/name` always blocks" promise false from a second direction. All four fixed and pinned.

**Two checks were silently not running.** `validate-provenance.sh` checks 7-8 were dead across nine skills. Waking them exposed a deeper problem: they assume `Research doc:` names a source index, but 30 of 121 entries point at topic content documents, so every new check-7 INFO was a false positive and check 8 was saved from a false-FAIL flood only by an *unannounced* skip. Checks 7/8 are now scoped to source indexes and every skip announces itself (#121).

**The retrofit's own anti-goal, four times.** ADR-0020 warns that a blunt gate gets satisfied by deleting content rather than relocating it. `diagnose` and `skill-audit` relocated prose and then read it unconditionally; `prototype` and `vale-config` deleted rules outright that survived nowhere. All four addressed.

## Verification

- `bash tests/run-tests.sh --strict` — 24 suites, 0 skipped, 0 failed
- `bash tests/run-bats.sh` — 325 tests, 0 failures
- `pre-commit run --all-files` — 17/17
- `pre-commit run --hook-stage pre-push --all-files` — 16/16, with `apm marketplace check` and `apm pack --check-clean` run against the remote, not skipped
- `scripts/skill-size-check.sh` over all 39 skills — rc 0, 0 ERROR/FAIL, SUGGESTION-only
- Preload tax measured at **10,498 chars**, max description 390 — both inside budget
- Every new test proven non-vacuous by a deliberate mutation of the behaviour it covers

**Per-commit sync, stated accurately:** the ten commits from the latest review round each pass `check-plugin-content-sync` in isolation, verified by checking each out in a detached worktree with a clean between. The earlier gitea window (`dfacf05..bedbd1d`, nine commits) does **not** — its mirror was regenerated in one batch at `bbc7300`. An earlier revision of this description claimed the property held for every commit; it does not, and a bisect through that window lands on a red commit. **Squash-merge** to collapse it, or accept that this range is not bisectable.

## Version bump

Six plugins and the catalog take a **patch**, not a minor. The branch is **89 commits — 40 `fix` / 30 `refactor` / 12 `docs` / 5 `chore` / 2 `test` — zero `feat`, zero `!`, zero `BREAKING CHANGE`** — and adds no skill, agent, command or hook. (Two earlier revisions of this section cited a stale histogram, most recently 78 commits; the figures above are measured at HEAD.) Both rules this repo ships (`forge/references/version-bump.md`, landing in this PR, and `git-commits/references/conventional-commits-spec.md`) make that a patch, and the catalog set is unchanged at 7 entries.

Not settled by that: four published files were removed from the installed tree, three moved, and `caveman` gained `disable-model-invocation`, retiring its old triggers. Under a strict reading those are major-class and currently ship under `refactor:` with no marker. Whether the deployed skill surface is a public contract is written down nowhere — worth deciding, but it outlives this PR.

## Deliberately not in scope

#112 (cherry-pick ownership, now resolved in favour of `git-commits`), #113 (`rtk git` normalisation), #116 (research fan-out), #101 (audit-skill merge), #122 (non-spec skill-root files), #123 (no PRD producer) stay open. #117 is the one worth reading: the contract's remedy is to move prose into `references/`, which is exactly where neither the size gate nor Vale looks — and the blind spot is wider than #117 currently records, since there is no root `.vale.ini` at all, so every ADR, `CONTEXT.md` and `README.md` is unlinted too.

That blind spot let this branch carry two `level: error` `Kyberforge.SentenceOpenerThereIs` violations into `references/` files it created — `provider-adapter-author/references/provider-matrix.md:31` and `agent-audit/references/finding-criteria.md:95`. Both are reworded in `afadaae`, confirmed by routing each file through the audit's own `vale-wrap.sh` (1 error each before, 0 after). Five further occurrences sit in `references/` files already on `main`; those are the pre-existing corpus and stay with #117, which is the real fix.

Also unfixed and not this PR's: `apm install` appends a duplicate `SessionStart` entry to `.claude/settings.json`, so a fresh clone cannot get pre-push green without an edit AGENTS.md warns against. Reproduces identically on `main`.

Co-authored-by: Defame1297 <gitea@rkdr.net>
Reviewed-on: https://git.dev.rkdr.net/Defame1297/holocron/pulls/129
Co-authored-by: Claude Code AI - Gitea MCP <claude@noreply.git.dev.rkdr.net>
Co-committed-by: Claude Code AI - Gitea MCP <claude@noreply.git.dev.rkdr.net>
This commit was merged in pull request #129.
This commit is contained in:
Claude Code AI - Gitea MCP
2026-09-01 13:47:46 +00:00
committed by Defame1297
parent 0e91a3ae66
commit 598a7c326a
420 changed files with 15303 additions and 4740 deletions

View File

@@ -0,0 +1,42 @@
---
source_keys:
- claude-code-subagents-docs
---
# Routing a plugin or marketplace entry to apm-workflow
Reached from `SKILL.md` Step 2 when the classified artifact is a plugin or a marketplace entry.
Both route to `apm-workflow` — a plugin to its configure flow (`apm plugin init`), a marketplace
entry to its marketplace flow (`apm marketplace package add`).
No other skill is a candidate for these two rows: `plugin-author` and `marketplace-author` were
removed per ADR-0015 once issue #90 landed, and `apm-workflow` is their sole successor.
## Always inline, never forked
Run these routes inline, in the current conversation. Their flows are short, prompt-heavy or
gated — `apm-workflow`'s publish and release steps take a HITL gate, and removing a marketplace
entry takes a conversational confirmation — and a backgrounded fork cannot surface those
checkpoints to the user in real time.
## No clean-context recheck, and no automatic audit
Skill and agent routes close with a clean-context audit rerun; these two do not, and the omission
is deliberate rather than an oversight. Neither artifact type has an audit skill counterpart to
re-run, so detaching the route to earn a recheck it would never get buys nothing.
These routes get no automated terminal check either. `apm audit` is a separate action on
`apm-workflow`'s own dispatch table, not a closing step of the configure or marketplace flow a
forge route lands in, so a completion message from either says nothing about it. Do not wait for
one and do not report one you did not see.
Verify by hand instead. Read back what the route wrote against what the grill settled:
- **Plugin** — the package directory exists where the intent said it should, and its `apm.yml`
carries the intended `name`, a top-level `type:` field, and a `version`.
- **Marketplace entry** — the entry names that package, points at the source the intent settled
on, and carries the version the package actually declares.
If the change warrants the full integrity and policy check rather than a read-back, invoke
`apm-workflow` again for its audit action and run `apm audit` deliberately. Then return to
`SKILL.md` Step 3 for the closing gates common to every route.

View File

@@ -0,0 +1,44 @@
---
source_keys:
- claude-code-subagents-docs
---
# Routing a skill or agent to its author skill
Reached from `SKILL.md` Step 2 when the classified artifact is a skill or an agent/subagent
definition. Route a skill to `skill-author` and an agent to `agent-author`. The two branches
differ on one axis only — which audit skill verifies the result — and everything below applies to
both.
## Choose fork or inline
Default to a **fork subagent**. It inherits the full grilled-intent conversation, so the author
skill does not need re-briefing on what the user asked for or what the grill settled.
Fall back to an **inline invocation** — same conversation, no subagent — when either holds:
- **Fork is technically unavailable.** You are already running inside a fork (a fork cannot spawn
another fork), a nesting-depth cap is reached, or the environment does not support forking.
- **The routed flow needs live user interaction mid-run** that a backgrounded fork cannot surface
in real time: clarifying questions, confirmation checkpoints, or a HITL gate. Judge this from
context — if nothing about the flow signals a live checkpoint, prefer the fork.
## Two-tier verification
Both author skills already close out with their own inline audit, in the same context as the
authoring work: `skill-author` runs `/skill-audit`, `agent-author` invokes
`agent-audit`. That is tier one, and forge does not change it.
Tier two belongs to forge. Once the author skill's run has finished, spin up a separate
**clean-context subagent** — fresh, not forked, no inherited context — to independently re-run the
same audit skill against the finished artifact. This is a distinct verification layer, not a
duplicate: the inline audit shares context with the work it is checking and can share its blind
spots, while the clean rerun has no stake in the result.
If the clean audit surfaces any unresolved finding — not only a disagreement with the inline pass,
any actionable finding on its own — loop: re-invoke the author skill (same fork-versus-inline
judgment as the first invocation) to resolve it, then re-run the clean audit. Repeat until the
clean audit comes back with nothing unresolved. Only then is the route done. This is the same
resolve-before-close discipline the author skills already apply to their own inline audit.
Return to `SKILL.md` Step 3 for the closing gates common to every route once the loop closes.

View File

@@ -4,15 +4,15 @@
- **URL:** https://code.claude.com/docs/en/sub-agents
- **Research doc:** plugins/kyberforge/docs/research/docs/claude-code-plugins/sources.md
- **Description:** Official Claude Code subagent reference — definition format, all frontmatter fields, scope priority, built-in agents, CLI flags, environment variables, known limitations. Grounds Step 3's fork-vs-inline invocation logic: fork inherits full conversation history via `/fork` or `subagent_type: "fork"`, is not a declarable frontmatter field on any agent definition, cannot be nested (a fork cannot spawn another fork), and is a caller-side invocation choice rather than a property of the artifact being routed to.
- **Contributing files:** SKILL.md
- **Description:** Official Claude Code subagent reference — definition format, all frontmatter fields, scope priority, built-in agents, CLI flags, environment variables, known limitations. Grounds the fork-vs-inline invocation logic in `references/author-routes.md`, the always-inline decision for the apm routes in `references/apm-routes.md`, and the clean-context bump subagent in `references/version-bump.md`: fork inherits full conversation history via `/fork` or `subagent_type: "fork"`, is not a declarable frontmatter field on any agent definition, cannot be nested (a fork cannot spawn another fork), and is a caller-side invocation choice rather than a property of the artifact being routed to.
- **Contributing files:** SKILL.md, references/author-routes.md, references/apm-routes.md, references/version-bump.md
- **Status:** `extracted`
## context7-websites-code-claude
- **URL:** context7:/websites/code_claude
- **Research doc:** plugins/kyberforge/docs/research/docs/claude-code-plugins/sources.md
- **Description:** Official Claude Code documentation site indexed by Context7 — confirms the `context: fork` skill-level frontmatter field means isolated/fresh execution, the opposite of what the `/fork` subagent command does (inherits conversation). Informs the Gotchas entry warning against conflating the two.
- **Description:** Official Claude Code documentation site indexed by Context7 — confirms the `context: fork` skill-level frontmatter field means isolated/fresh execution, the opposite of what the `/fork` subagent command does (inherits conversation). Informs the Gotchas entry in `SKILL.md` warning against conflating the two; nothing else in this skill draws on it, and no `references/` file mentions the `context: fork` field.
- **Contributing files:** SKILL.md
- **Status:** `extracted`
@@ -28,7 +28,7 @@
- **URL:** https://agentskills.io/specification.md
- **Research doc:** plugins/kyberforge/docs/research/docs/agentskillsio/sources.md
- **Description:** Complete SKILL.md format specification — confirms `assets/`, `references/`, and `scripts/` are warranted only by the bulk/reusability of supporting content (large reference material, executable code, templates), not by a skill's category. forge has none of that bulk, so a lean SKILL.md-plus-provenance-file shape is spec-legitimate; the `references/sources.md` in this directory exists for this repo's own provenance-chain convention (see `CONTEXT.md`), not because the spec requires it.
- **Description:** Complete SKILL.md format specification — confirms `assets/`, `references/`, and `scripts/` are warranted only by the bulk/reusability of supporting content (large reference material, executable code, templates), not by a skill's category, and forge's per-route procedures are that kind of supporting content — so the spec permits the `references/` split here but does not require it. The warrant is a house decision: ADR-0020's rule that dispatch is mandatory at two or more mutually exclusive flows, which forge's four-row table is. `references/sources.md` likewise exists for this repo's own provenance-chain convention (see `CONTEXT.md`), not because the spec requires it.
- **Contributing files:** SKILL.md
- **Status:** `extracted`

View File

@@ -0,0 +1,40 @@
---
source_keys:
- claude-code-subagents-docs
---
# Bumping the package version after a route
Reached from `SKILL.md` Step 3 after a route has finished. A skill route always lands here:
`skill-author` moves only a skill's own `metadata.version`, which is not the package manifest's
number, so the package version is still behind when it reports done. `agent-author` bumps the
resolved package's `apm.yml` itself at plugin/APM scope, and `apm-workflow`'s configure flow
carries the same policy — read those routes' output before acting here, because a second bump for
one change is wrong.
## Find the owning package
Walk up from the artifact's path to the nearest ancestor `apm.yml` that declares a top-level
`type:` field (`instructions`, `skill`, `hybrid` or `prompts`).
An `apm.yml` with **no** `type:` field is a marketplace-only manifest: it lists packages rather
than declaring one, so it does not count as a match. Skip it and keep walking up.
Skip this step entirely if no ancestor `apm.yml` carries a `type:` field: the artifact is then
standalone or scoped to a user agent directory, and there is no package to version.
## Delegate the bump
Invoke `apm-workflow` as a **clean-context subagent** — fresh, not forked — with this
brief:
> "The package at `<package-path>` gained a new `<artifact-type>` (`<artifact-name>`). Bump the
> `version` field in that package's `apm.yml`. Determine whether to bump minor (0.1.0) or patch
> (0.0.1) based on whether this is a new capability (minor) or a fix/refactor (patch). Do not
> release or tag — just update `apm.yml` and commit."
Clean context rather than a fork is the point: the bump decision is made independently, without
anchoring on the authoring conversation that just argued for the artifact's significance.
Then report to the user: "Updated `<package-name>` version from `<old>` to `<new>` to reflect the
new `<artifact-name>`."