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:
2026-08-14 21:13:13 +00:00
parent 1c6eababb0
commit 4a5c3c0cff
104 changed files with 6272 additions and 1880 deletions

View File

@@ -1,7 +1,6 @@
---
source_keys:
- context7-websites-code-claude
- claude-code-plugins-docs
- claude-code-subagents-docs
- context7-github-en-copilot
- github-custom-agents-configuration
@@ -9,41 +8,120 @@ source_keys:
# Agent Description Quality Reference
Load this file when a description finding is borderline and you need to make a precise call.
Upstream source: Claude Code subagent reference, GitHub Copilot custom-agents configuration.
House contract: ADR-0020, the context budget. The house contract is narrower than either
platform's schema rather than a reinterpretation of it: where both speak, both must be satisfied.
## Action-verb opening
## Why the description is the expensive part
The description must open with an imperative or present-tense verb that describes what the agent does ("Reviews...", "Audits...", "Generates...", "Analyzes..."). Avoid:
- Noun phrases: "An agent that..." — no verb
- "This agent..." or "Use this when..." — passive framing
- "Helps with..." — too vague to be a clear verb
At startup an agent loads only the `name` and `description` of every installed skill and agent.
The body is never seen until the agent is invoked. The description therefore carries the entire
triggering burden **and** is paid for in every session, whether the agent fires or not.
**Borderline call:** "Validates and reviews..." is acceptable — two verbs is fine if both are specific. "Assists in reviewing..." is not — "assists" is vague filler.
A second cost is less obvious and is a correctness hazard rather than a token cost: a description
that summarises the workflow is a shortcut the caller takes *instead of* reading the body. A
measured failure upstream — a description saying "code review between tasks" — produced one review
where the body's flowchart specified two.
## Specificity of trigger condition
## Step 0 — establish which contract applies
The description must state what specifically triggers the agent. Generic phrasing fails:
- Too vague: "when the user needs help with agents"
- Acceptable: "when the user says 'audit this agent', 'check if my agent follows best practices', or wants to know if an agent pair is ready to ship"
Read the frontmatter before judging a single word.
Include indirect triggers: "even if they don't use the word 'audit'" or "even if the user doesn't phrase it as a review request". If the agent should activate on a recognisable user goal (not just literal keyword matches), name that goal.
- **`disable-model-invocation: true` or `user-invocable: false`** — the agent is hand-invoked. Its
description is never matched against user intent, so it is not a routing string. It carries **one
plain human-facing sentence** stating what the agent does. Audit it for that and nothing else.
Reporting a missing trigger clause, a missing boundary clause or absent indirect triggers on a
hand-invoked agent is a wrong finding, not a strict one. Both fields are Copilot-only and neither
is on the vendor-neutral APM allowlist, so this case arises in a Copilot `.agent.md` at
project/user scope and nowhere else. 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.
- **No such flag** — the agent is model-invoked and the rest of this file applies.
**Borderline call:** If the description covers direct triggers but omits common indirect phrasings that a user would plausibly use, mark as SUGGESTION (not FAIL) — the agent still activates, just less reliably.
## The three-part shape
## `Use proactively`
A model-invoked description carries exactly three things:
For CC files: including "Use proactively" signals the CC runtime to offer the agent unprompted when conditions are met. This is CC-specific — use it when the agent should activate without an explicit user request.
1. **Trigger clause.** When to invoke, phrased imperatively: `Use when ...`. Not `This agent ...` —
the caller is deciding whether to act, not reading a catalogue entry.
2. **At most one capability clause.** What it does, in one clause. Never an enumeration.
3. **Boundary clause.** Compressed form: `Not <thing> -> <skill-name>.` The target must resolve to
a real skill directory or agent file in the authoring source.
For Copilot files: this phrase has no effect. Use `user-invocable: false` / `disable-model-invocation: true` for equivalent Copilot behavior. Flag `Use proactively` in a Copilot description as a SUGGESTION (not FAIL) — it causes no harm, just has no effect.
Everything else belongs in the body or in the plugin's `README.md`.
## Length and hard limits
## Indirect triggers — conditional, never blanket
- CC agent descriptions: no documented character limit, but keep under 500 characters to avoid truncation in UI contexts.
- Copilot agent descriptions: no separate documented limit, but the overall 30,000-character body limit applies to the full file.
- Skill descriptions (SKILL.md): hard 1024-character limit enforced by the platform.
Add "even if the user doesn't say X" **only where the user's natural phrasing genuinely omits the
domain word.** True for the `gitea-*` family: people say "create an issue", not "create a Gitea
issue". False for `git-commits`: nobody asks for a commit without saying commit. A blanket
indirect-trigger clause on an agent whose domain word is unavoidable is padding charged to every
session.
## Do not use when
## Near-miss exclusions
Include a "Do not use when..." clause only if a near-miss agent or skill exists that could steal activations. Omitting it is not a finding. Including it is correct when there is a real confusion risk (e.g., `/agent-audit` vs `/skill-audit`).
Add a boundary clause only where a sibling skill or agent could plausibly steal the activation. Use
strong near-misses — queries that share keywords but need something different — not weak ones. One
boundary clause per genuine near-miss; a list of four is enumeration wearing a boundary's clothes.
**Borderline call:** If the "Do not use when" clause is present but the exclusion described is already obvious from context, mark as SUGGESTION to tighten or remove — not FAIL.
## Before / after
```yaml
# FAIL — a noun-phrase opener rather than a trigger, capability enumeration in
# place of one capability clause, and no boundary clause at all, preloaded into
# every session forever. (The live git-orchestrate description, 254 chars.)
description: Orchestrates git workflow operations for other agents. Invoke when a
caller needs a multi-step or destructive git operation (rebase, force-push, branch
deletion) coordinated across domain skills with safety gates, session context, and
structured results.
# PASS — trigger, one capability clause, boundary. The operation list and the
# safety-gate mechanics are the body's job; the router cannot act on them.
description: >
Use when an agent caller needs a multi-step or destructive git operation
dispatched and safety-gated. Not conversational git help -> git-workflow.
```
## Auditing guidance
Flag as FAIL if:
- **Over 400 characters.** Measured on the folded YAML value, not the raw source lines.
`validate.sh` reports the number; do not re-derive it, but do point the Fix at what to cut. Agent
descriptions have no platform-documented ceiling of their own — unlike a skill's 1,024-character
spec limit, the 400-character house ceiling is the only hard limit there is, so do not go looking
for a backstop behind it.
- **Internal mechanics appear in the description.** Any of:
- capability enumeration or a feature list;
- output-format detail ("Produces a compact findings report with Why and Fix per finding");
- composition or architecture notes ("composes X rather than duplicating Y", "a cross-cutting
shared agent", "the human-facing entry point", "replaces the old flat invocation");
- implementation detail ("self-validates via a bundled deterministic script").
None of it can change a routing decision and all of it is preloaded.
`Kyberforge.CompositionNote` catches the common phrasings deterministically; the rest is
judgment. This is the rule that deflates a description, so apply it before reaching for length.
- **The same trigger stated twice in two registers** — a verb list, then the same verbs re-quoted
as user phrasings, usually in the same order. One register, whichever routes better.
- **Descriptive rather than imperative phrasing** (`This agent ...`, `This is the ...`).
`Kyberforge.DescriptionOpener` catches any opener matching `^This`. There is no action-verb rule
here and never was a defensible one: an `Orchestrates ...` or `Audits ...` opener is a catalogue
entry, not a trigger.
- **Vague capabilities** ("helps with agents" where "audits an agent definition pair" was
available). `Kyberforge.VagueWording` catches the known filler; imprecision outside that list is
judgment.
- **A boundary clause naming a target that does not resolve** to a real skill directory or agent
file in the authoring source. No script checks this for an agent file — `validate.sh` resolves
boundary targets for skills only, so resolve the name yourself against `plugins/*/.apm/skills/`
and `plugins/*/.apm/agents/`.
- **`Use proactively` in a Copilot or vendor-neutral description.**
`KyberforgeCopilot.ProactivePhrase` catches it. The phrase steers the Claude Code runtime and
does nothing anywhere else, so in a `.agent.md` it is preloaded text that buys no behaviour.
- **Trigger-list, boundary or indirect-trigger content on a hand-invoked agent** — see Step 0.
Flag as SUGGESTION if:
- **Over 250 characters** but at or under 400. This tier is what moves the corpus average; the FAIL
tier only stops outliers. Report it rather than treating a 399-character description as clean.
- A near-miss exclusion is present but targets a weak near-miss.
- An indirect trigger is present and warranted but could name the omitted phrasing more precisely.