feat(kyberforge): enforce the ADR-0020 context contract for skills and agents
Skill name+description pairs are preloaded into every session, costing ~6,200 tokens across 39 skills before any skill is invoked. The authoring rules mandated that growth: skill-author:104 and description-quality.md:21 both required padding, while skill-author:102 (the deflating rule) had no FAIL condition behind it. Gates (blocking, no baseline file): - description 250 chars SUGGESTION / 400 FAIL, measured on the folded YAML value - body-only 600 words SUGGESTION / 900 FAIL, independent of the unchanged whole-file 2770-word / 500-line spec backstop - every boundary-clause routing target must resolve to a real skill or agent; catches skill-improve, neuledge-context and gitea-labels - agents take the description gates but deliberately no body gate; a test pins that absence Vale: DescriptionOpener widened to ^This\b, new CompositionNote rule banning architecture notes from descriptions. 10 hits, 0 false positives. Kyberforge's own four skills retrofitted: descriptions 3,364 -> 938 chars (-72%), bodies 8,306 -> 2,487 words (-70%), all via the apm-workflow dispatch pattern. Fixes the skill-improve dangling route and the agent-author misroute to manual review. Also fixes a pre-existing false positive where any line-initial 'read ' was flagged as interactive input, which had already caused two scripts to be rewritten around it. Refs: ADR-0020
This commit is contained in:
187
plugins/kyberforge/skills/skill-author/references/contract.md
Normal file
187
plugins/kyberforge/skills/skill-author/references/contract.md
Normal file
@@ -0,0 +1,187 @@
|
||||
---
|
||||
source_keys:
|
||||
- agentskills-spec
|
||||
- agentskills-best-practices
|
||||
- agentskills-optimizing-descriptions
|
||||
---
|
||||
|
||||
# The description and body contract
|
||||
|
||||
House contract, set by ADR-0020. Every rule here is enforced by `/skill-audit` —
|
||||
`scripts/validate.sh` for the counts and the boundary targets, the bundled Vale styles for the
|
||||
prose patterns, and its reference files for the judgment calls.
|
||||
|
||||
## Why the budget exists
|
||||
|
||||
A skill's `name` and `description` are loaded into every agent's context at the start of every
|
||||
session, whether or not the skill is ever invoked. The body is loaded only on invocation, and then
|
||||
competes with the caller's live conversation. Those are two different costs, so they get two
|
||||
different ceilings — and a fat description is not merely expensive. A description that summarizes
|
||||
the workflow gets followed *instead of* the body: a description saying "code review between tasks"
|
||||
produced one review from a skill whose flowchart specified two.
|
||||
|
||||
## Description
|
||||
|
||||
A description carries exactly three things:
|
||||
|
||||
1. **Trigger clause** — when to invoke, imperative: "Use when ...", never "This skill ...".
|
||||
Focus on user intent, not the skill's internal mechanics.
|
||||
2. **At most one capability clause** — what it does, one clause, no enumeration. Be specific
|
||||
("parses and validates OpenAPI specs", not "helps with APIs").
|
||||
3. **Boundary clause** — form: `Not <thing> -> <skill-name>.` Add one only where a near-miss skill
|
||||
could steal activations.
|
||||
|
||||
Banned from a description; move it to the body or to `README.md`:
|
||||
|
||||
- Capability enumeration or feature lists
|
||||
- Output-format detail ("Produces a compact findings report with Why and Fix per finding")
|
||||
- Composition or architecture notes ("composes X rather than duplicating Y", "This is a
|
||||
cross-cutting shared skill", "the human-facing entry point")
|
||||
- Implementation detail ("Self-validates via a bundled deterministic script")
|
||||
- Restating the same trigger twice in two registers — a verb list, then the same verbs re-quoted
|
||||
as user phrasings. This is a FAIL, not a suggestion.
|
||||
|
||||
**Indirect triggers are conditional, not mandatory.** Add "even if the user doesn't mention X
|
||||
explicitly" only where the user's natural phrasing genuinely omits the domain word — true for the
|
||||
`gitea-*` family, because people say "create an issue" rather than "create a Gitea issue"; false
|
||||
for `git-commits`, where the user says "commit". Adding one everywhere is what inflated this
|
||||
corpus, and it was deleted as a blanket rule.
|
||||
|
||||
**Boundary targets must resolve.** The name after the arrow is checked against real skill
|
||||
directories under `plugins/*/.apm/skills/<name>/` and real agents under
|
||||
`plugins/*/.apm/agents/<name>.agent.md`. A boundary clause naming a target that does not exist
|
||||
sends the router nowhere and fails the audit. Check the target exists before writing it — do not
|
||||
invent a plausible sibling name.
|
||||
|
||||
**Length.** 250 characters SUGGESTION, 400 characters FAIL, counting the frontmatter value only
|
||||
with YAML folding resolved. The agentskills.io 1,024-character spec limit is unchanged and sits
|
||||
above both. The SUGGESTION tier is the one that moves the average; treat 250 as the target and 400
|
||||
as the outlier stop.
|
||||
|
||||
**Hand-invoked skills are exempt.** A skill carrying `disable-model-invocation: true` is absent
|
||||
from the model-visible listing and is reached only by the user typing `/name`. It takes one plain
|
||||
human-facing sentence — no trigger clause, no boundary clause, no indirect triggers. Worked
|
||||
example: `plugins/bin/.apm/skills/zoom-out/SKILL.md`.
|
||||
|
||||
## Body
|
||||
|
||||
The body carries the **decision procedure only**: ordered steps, decision branches, gates, and
|
||||
which reference to load when. Everything else moves to `references/`.
|
||||
|
||||
Ask of every sentence: "Would the agent get this wrong without it?" Cut anything that answers "no."
|
||||
|
||||
Include:
|
||||
|
||||
- Non-obvious sequences or ordering constraints — the agent may skip or reorder steps without this
|
||||
- Domain conventions the agent cannot infer from general knowledge — the core value a skill adds
|
||||
- One default per decision point, plus one escape hatch — never a menu; menus cause the agent to
|
||||
pause or pick arbitrarily
|
||||
- Gotchas — facts that defy reasonable assumptions
|
||||
|
||||
Exclude:
|
||||
|
||||
- Concepts the agent already knows (what JSON is, how HTTP works) — tokens without behavior change
|
||||
- Exhaustive option lists — pick a default; the agent does not benefit from choosing
|
||||
- Steps the agent handles independently — over-specifying leads agents down unproductive paths
|
||||
- Restatements of the description — it is already in context
|
||||
|
||||
Move to `references/`: lookup tables, spec restatements, output schemas, templates, example
|
||||
blocks, rationale prose, and any content only one branch reaches. Each reference file is
|
||||
self-contained for its concern, and every one is wired from the body with the literal conditional
|
||||
form:
|
||||
|
||||
````markdown
|
||||
If <condition>, read `references/<file>.md`.
|
||||
````
|
||||
|
||||
A generic pointer ("see references/ for details") is a Vale error — the agent cannot act on it.
|
||||
|
||||
**Dispatch is mandatory at two or more mutually exclusive flows.** The body carries the dispatch
|
||||
table and the gates common to every branch; each flow gets its own self-contained `references/`
|
||||
file. Exemplar: `plugins/kyberforge/.apm/skills/apm-workflow/SKILL.md` — a 554-word body
|
||||
dispatching to 3,006 words of references.
|
||||
|
||||
**Length.** 600 words SUGGESTION, 900 words FAIL, counting the **body only** — everything after
|
||||
the frontmatter's closing `---`.
|
||||
|
||||
## Gotchas section
|
||||
|
||||
- Each entry must state a fact that **contradicts a reasonable default** — something the agent
|
||||
gets wrong by acting sensibly. "Never commit secrets" is not one; the agent already knows.
|
||||
- Maximum five entries.
|
||||
- A Gotcha that paraphrases a step in the body below it is a **FAIL**. If the rule is already a
|
||||
step, it is not a gotcha.
|
||||
- A Gotchas section exceeding 25% of the body is a SUGGESTION.
|
||||
- Place the section near the top — a gotcha read after the mistake is worthless.
|
||||
|
||||
## Two size gates, two measurements
|
||||
|
||||
| Gate | SUGGESTION | FAIL | Counts |
|
||||
|---|---|---|---|
|
||||
| description | 250 chars | 400 chars | the `description:` value only |
|
||||
| body | 600 words | 900 words | the body only, after the closing `---` |
|
||||
| spec backstop | — | 1,024 chars | the `description:` value only |
|
||||
| spec backstop | — | 2,770 words / 500 lines | the **whole file**, frontmatter included |
|
||||
|
||||
The 600/900 pair and the 2,770/500 pair are not the same measurement and must not be unified: the
|
||||
first is a quality gate on what the caller's context absorbs, the second a conformance backstop on
|
||||
the file. A skill can sit well inside one and fail the other.
|
||||
|
||||
When a body approaches its ceiling, relocate rather than delete — move reference material to
|
||||
`references/<topic>.md` behind a conditional trigger, and bundle repeated executable logic into
|
||||
`scripts/` rather than reinventing it each run.
|
||||
|
||||
## Body patterns
|
||||
|
||||
**Default with escape hatch** (not a menu):
|
||||
|
||||
````markdown
|
||||
Use <X> for <task>. For <edge case>, use <Y> instead.
|
||||
````
|
||||
|
||||
**Prescriptive sequence** (when order is critical or fragile):
|
||||
|
||||
````markdown
|
||||
Run exactly:
|
||||
```bash
|
||||
<command>
|
||||
```
|
||||
Do not modify flags.
|
||||
````
|
||||
|
||||
**Checklist** (multi-step workflows):
|
||||
|
||||
````markdown
|
||||
- [ ] Step 1: ...
|
||||
- [ ] Step 2: ...
|
||||
````
|
||||
|
||||
**Dispatch table** (two or more mutually exclusive flows):
|
||||
|
||||
````markdown
|
||||
| Condition | Flow | Reference |
|
||||
|---|---|---|
|
||||
| <condition> | <flow> | `references/<file>.md` |
|
||||
````
|
||||
|
||||
**Output format template** (when the skill produces structured output):
|
||||
|
||||
````markdown
|
||||
Output format:
|
||||
```
|
||||
<field>: <value>
|
||||
```
|
||||
````
|
||||
|
||||
For longer templates, place them in `assets/<name>.md` and reference conditionally.
|
||||
|
||||
## Embedding org-specific policy
|
||||
|
||||
If a skill encodes a rule sourced from an org convention file (e.g. `core/instructions/*.md`),
|
||||
inline that content directly into the skill (SKILL.md or a `references/` file) rather than pointing
|
||||
to the file's path. Plugins must be self-contained and portable — the org file may not exist
|
||||
wherever the plugin is installed, and in this repo such files are meant to be deleted once their
|
||||
content is fully embedded downstream. Tag the inlined content with a `source_keys` entry using the
|
||||
same `references/sources.md` schema as the create flow's Step 6, noting in the `Research doc:`
|
||||
field that the source is an org convention rather than a plugin research corpus entry, so
|
||||
provenance survives after the source file is gone.
|
||||
Reference in New Issue
Block a user