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:
@@ -1,16 +1,12 @@
|
||||
---
|
||||
name: forge
|
||||
description: >
|
||||
Use when the user wants to build, add, or improve something
|
||||
but hasn't yet named which of it (skill, agent, plugin, or marketplace
|
||||
entry) they need — "I want to add something to kyberforge", "not sure if
|
||||
this should be a skill or a plugin", "help me figure out what to build",
|
||||
"I have an idea but don't know where it belongs". Grills the intent first,
|
||||
classifies the target artifact type, then routes to the matching author
|
||||
skill. Do not use when the user already names the target artifact type or
|
||||
skill/agent explicitly (e.g. "run /skill-author on my-skill", "create an
|
||||
agent for X") — route directly to that author skill instead, bypassing
|
||||
forge.
|
||||
Use when the user wants to build or improve something but has not yet named
|
||||
the artifact type — skill, agent, plugin, or marketplace entry; "not sure if
|
||||
this should be a skill or a plugin", "I have an idea but don't know where it
|
||||
belongs". Routes to the matching author skill. Do not use when the type is
|
||||
already named — invoke `skill-author`, `agent-author` or `apm-workflow`
|
||||
directly.
|
||||
metadata:
|
||||
category: factory
|
||||
source_keys:
|
||||
@@ -21,62 +17,37 @@ metadata:
|
||||
|
||||
## Gotchas
|
||||
|
||||
- forge is an optional guided entry point, not a gate — the four existing factory skills (`skill-author`, `skill-audit`, `agent-author`, `agent-audit`) plus `apm-workflow` (for plugin/marketplace-entry artifacts) remain directly invokable and forge does not intercept those calls. `plugin-author` and `marketplace-author` were removed per ADR-0015 once issue #90 landed — `apm-workflow` is their sole successor.
|
||||
- Claude Code's skill-level `context: fork` frontmatter field and the `/fork` subagent command are opposites despite sharing a name — `context: fork` isolates (fresh context, no parent access), while `/fork` inherits the full conversation. Keep this straight when deciding how to invoke a subagent in Step 3.
|
||||
- forge is an optional guided entry point, not a gate — `skill-author`, `skill-audit`, `agent-author`, `agent-audit` and `apm-workflow` all stay directly invokable, and forge never intercepts a direct call to one.
|
||||
- Claude Code's skill-level `context: fork` frontmatter field and the `/fork` subagent command are opposites despite the shared word: `context: fork` isolates (fresh context, no parent access), while `/fork` inherits the full conversation. The route reference each classification loads spends that distinction: `references/author-routes.md` chooses between the two, `references/apm-routes.md` rules the fork out.
|
||||
|
||||
## Step 1 — Grill the intent
|
||||
|
||||
Call `bin:grill-with-docs` unless a grill session was already performed and is available in the context.
|
||||
Grilling may surface that the artifact type assumed at the start is wrong, or that the idea splits into more than one artifact.
|
||||
This step always runs inline, in the current conversation — grilling is interactive and a subagent cannot hold the back-and-forth.
|
||||
Call `grill-with-docs` unless a grill session has already run and is available in the context.
|
||||
|
||||
## Step 2 — Classify the artifact type
|
||||
`grill-with-docs` ships in a sibling plugin that kyberforge does not declare as an apm dependency, so it resolves in the authoring monorepo but can be absent where kyberforge is installed alone. If it does not resolve, grill inline yourself rather than skipping the step: what problem the artifact solves, who invokes it and how, what it must refuse, and which existing skill or plugin already owns part of the job. Say which path you took.
|
||||
|
||||
Match the grilled intent against exactly one row (or more than one, if the intent genuinely spans several):
|
||||
Grilling regularly overturns the artifact type assumed at the start, or splits one idea into several artifacts, so it runs before classification rather than confirming it. Run it inline in the current conversation — grilling is interactive and a subagent cannot hold the back-and-forth.
|
||||
|
||||
| Intent | Artifact type | Route to |
|
||||
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -----------------------------| ---------------------------------|
|
||||
| A reusable capability or workflow the agent should load inline in the main conversation — triggered automatically by description-matching, not a fresh context, and free to bundle its own `references/`, `scripts/`, or `assets/` | Skill | `kyberforge:skill-author` |
|
||||
| A recurring task needs its own reusable agent/subagent definition — dedicated system prompt, tools, and description, invokable by name across sessions | Agent / subagent definition | `kyberforge:agent-author` |
|
||||
| A new distributable unit is needed — no existing plugin is the right home for the skill/agent/hook/MCP server being built, or the bundle needs its own manifest, versioning, and install lifecycle separate from what already exists | Plugin | `kyberforge:apm-workflow` (configure — `apm plugin init`) |
|
||||
| The plugin itself already exists (or was just created) and only its marketplace-facing metadata needs to change — listing it for the first time, or updating its version/description entry — never the plugin's contents | Marketplace entry | `kyberforge:apm-workflow` (marketplace — `apm marketplace package add`) |
|
||||
## Step 2 — Classify and dispatch
|
||||
|
||||
If the intent is genuinely ambiguous between rows even after grilling, ask the user directly rather than guessing.
|
||||
Match the grilled intent against exactly one row — or more than one, if the intent genuinely spans several artifacts.
|
||||
|
||||
Note: plugin and marketplace-entry artifacts route through `kyberforge:apm-workflow` per ADR-0015 — the former `plugin-author` and `marketplace-author` skills were removed once issue #90 landed.
|
||||
| Intent | Artifact type | Route to | Read |
|
||||
|---|---|---|---|
|
||||
| A reusable capability the agent loads inline in the main conversation, triggered by description-matching, free to bundle its own `references/`, `scripts/` or `assets/` | Skill | `skill-author` | `references/author-routes.md` |
|
||||
| A recurring task needs its own reusable definition — dedicated system prompt, tools and description, invokable by name across sessions | Agent / subagent | `agent-author` | `references/author-routes.md` |
|
||||
| A new distributable unit — no existing plugin is the right home for the skill, agent, hook or MCP server being built, or the bundle needs its own manifest, versioning and install lifecycle | Plugin | `apm-workflow` (`apm plugin init`) | `references/apm-routes.md` |
|
||||
| The plugin already exists and only its marketplace-facing metadata changes — a first listing, or a version/description update, never the plugin's contents | Marketplace entry | `apm-workflow` (`apm marketplace package add`) | `references/apm-routes.md` |
|
||||
|
||||
This table classifies what to build, not how to run it — a one-off task that merely needs an isolated vs. context-inheriting run (rather than a new, reusable definition) isn't an artifact at all; there's nothing here to route it to.
|
||||
The table classifies what to build, not how to run it: a one-off task that merely needs an isolated or context-inheriting run is not an artifact and has no row here. If the intent stays genuinely ambiguous between rows after grilling, ask the user rather than guessing.
|
||||
|
||||
## Step 3 — Announce, then route
|
||||
A real artifact that matches no row — a hook, an MCP server, an AGENTS.md, a research doc — has no route here. Say so, hand the user the skill that does own it, and never bend it into a row to make the table fit.
|
||||
|
||||
State the classification and which skill(s) will run before invoking anything.
|
||||
When the intent spans several rows, chain the routes in dependency order — an artifact that must exist on disk before another skill can target it goes first, so `apm-workflow` scaffolds the plugin directory before `skill-author` scaffolds a skill inside it.
|
||||
|
||||
**Invoking the author skill(s).** Default to a fork subagent — it inherits the full grilled-intent conversation, so the author skill doesn't need to be re-briefed. Fall back to an inline invocation (same conversation, no subagent) when either is true:
|
||||
- **Fork is technically unavailable** — already running inside a fork (a fork cannot spawn another fork), a nesting-depth cap is reached, or the environment doesn't support forking.
|
||||
- **The routed flow needs live user interaction mid-run** that a backgrounded fork can't surface in real time — clarifying questions, confirmation checkpoints, or a HITL gate (e.g. `apm-workflow`'s publish/release steps, or its conversational confirmation before removing a marketplace entry). Judge this from context: if nothing about the routed flow signals a live checkpoint, prefer the fork subagent.
|
||||
**Announce, then invoke.** State the classification and which skill(s) will run. Then read the reference file for each classified artifact type — only those — and follow it.
|
||||
|
||||
`apm-workflow` routes for plugin/marketplace-entry artifacts always run inline — their flows are short, prompt-heavy, or gated, and get no follow-up audit-recheck step to justify running detached (see below).
|
||||
## Step 3 — Closing gates, common to every route
|
||||
|
||||
**After a skill or agent route finishes.** `skill-author` and `agent-author` already close out with their own inline audit (`skill-author` runs `/skill-audit`, `agent-author` invokes `kyberforge:agent-audit` directly) in the same context as the authoring work — that's unchanged. Once that 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's 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-vs-inline judgment as the initial invocation) to resolve it, then re-run the clean audit again. Repeat until the clean audit comes back with nothing unresolved. Only then is the route done — the same resolve-before-close discipline `skill-author`/`agent-author` already apply to their own inline audit.
|
||||
|
||||
When the intent spans multiple artifact types (e.g. a new skill inside a new plugin, then registering that plugin via `kyberforge:apm-workflow` marketplace), chain the routes in dependency order — an artifact that must exist on disk before another skill can target it goes first (e.g. `apm-workflow` scaffolds the plugin directory via `apm plugin init` before `skill-author` scaffolds a skill inside it).
|
||||
|
||||
## Step 4 — Bump plugin version (if applicable)
|
||||
|
||||
After the routed skill finishes, check if the artifact was created or updated inside a package by walking up from the artifact's path to the nearest ancestor `apm.yml` that declares a top-level `type:` field (`instructions`/`skill`/`hybrid`/`prompts`). An `apm.yml` with no `type:` field is a marketplace-only manifest (see `plugins/kyberforge/docs/research/docs/microsoft-apm/monorepo-and-repo-shapes.md`) — it does not count as a match; skip it and keep walking up.
|
||||
|
||||
**Skip this step if:**
|
||||
- No ancestor `apm.yml` with a `type:` field is found (the artifact is standalone or scoped to user agent directories)
|
||||
- The author skill already bumped the package version (check the skill's audit output or completion message for version bump evidence)
|
||||
|
||||
**If a typed `apm.yml` is found and no version bump was done:**
|
||||
|
||||
Invoke `kyberforge: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."
|
||||
|
||||
Use a clean-context subagent (not forked) so the version bump decision is made independently without anchoring to the earlier authoring context. This gives apm-workflow a clear, isolated directive.
|
||||
|
||||
Report completion to the user: "Updated `<package-name>` version from X.Y.Z to X.Y.Z to reflect the new `<artifact-name>`."
|
||||
- **Resolve before closing.** A route is finished only when its verification reports nothing unresolved. An actionable finding reopens the route; it is never reported onward as a caveat.
|
||||
- **Bump the package version.** A skill route always lands here: `skill-author` moves only a skill's own `metadata.version`, which is not the package `apm.yml`'s number — so read `references/version-bump.md` after one. `agent-author` and the apm routes bump the package themselves at plugin scope; after those, read it only when their output does not say they did.
|
||||
|
||||
Reference in New Issue
Block a user