Files
holocron/plugins/kyberforge/.apm/skills/agent-audit/references/description-quality.md
Defame1297 3811f5481b 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
2026-09-09 05:15:23 +00:00

5.1 KiB

source_keys
source_keys
context7-websites-code-claude
claude-code-subagents-docs
context7-github-en-copilot
github-custom-agents-configuration

Agent Description Quality Reference

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.

Why the description is the expensive part

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.

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.

Step 0 — establish which contract applies

Read the frontmatter before judging a single word.

  • disable-model-invocation: true — 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. The field is Copilot-only and not 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. 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.

The three-part shape

A model-invoked description carries exactly three things:

  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.

Everything else belongs in the body or in the plugin's README.md.

Indirect triggers — conditional, never blanket

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.

Near-miss exclusions

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.

Before / after

# 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.

Where the criteria live

Every FAIL and SUGGESTION criterion for this dimension is in references/finding-criteria.md, which Step 3 reads on every run. This file is the reasoning behind them, loaded only when that file puts the description dimension in play.