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

@@ -0,0 +1,67 @@
---
source_keys:
- context7-websites-code-claude
- claude-code-plugins-docs
---
# Plugin/APM scope — the single vendor-neutral file
One file, no counterpart: `<package-root>/.apm/agents/<name>.agent.md`. `apm compile` emits it to
both the Claude Code and the Copilot CLI target. The `.agent.md` extension here is convention, not
a Copilot marker — the file is vendor-neutral.
## Frontmatter
The permitted keys are the `apm-agent-allowlist` section of `agent-audit`'s
`references/field-inventory.md`. Read them from there as data — that section is the single source
of truth, `agent-audit`'s `validate.sh` parses it at load time, and it changes. Any restatement of
the roster, here or in a template or in script output, goes stale one step further out than the
list itself.
- `name` — kebab-case, must equal the filename stem, unique within the scope.
- `description` — write it against `references/contract.md`.
- Everything else — check the allowlist section before adding a key. A key outside it fails the
audit.
**Why the list is narrow.** `apm compile` copies frontmatter verbatim to every target with no
per-target integrator, so a harness-specific value is wrong on at least one of them (ADR-0016).
The rule is about a field's *shape*, not a fixed roster:
- `tools` is an **allowlist** whose vocabulary differs per harness — Claude Code names its own
tools, Copilot CLI uses aliases (`execute`/`read`/`edit`/`search`/`agent`/`web`) — so one value
is wrong on one target. It stays out. Omitting it means inherit-all-tools on both, which is
never wrong.
- `disallowedTools` is a **denylist**, and denying by name cannot fail that way: a name the other
harness does not recognise denies nothing, so the worst case is a missing fence, never a wrongly
granted capability. That asymmetry is the whole exception (ADR-0016's 2026-08-14 amendment).
Claude Code honours it for plugin subagents; the three fields plugin agents do silently ignore
are `hooks`, `mcpServers` and `permissionMode`, and this is not one of them. Copilot's handling
of the key is unconfirmed, which ADR-0016 accepts as a stated risk.
- The Claude-only knobs (`isolation`, `maxTurns`, `effort`, `memory`, `permissionMode`, `skills`,
`color`, `initialPrompt`, `background`, `hooks`, `mcpServers`) have no Copilot equivalent and
are never written to this file at all. "Silently ignored at plugin scope" is the wrong framing:
they are absent, not tolerated. To use any of them, copy the agent to `.claude/agents/`
(project scope) or `~/.claude/agents/` (user scope).
Write `disallowedTools` on every read-only plugin-scope agent — and say the agent is read-only in
the body as well, because the fence does not cover the inherited `Bash` tool.
`source_keys` is provenance metadata, not a runtime field: both harnesses ignore it. Add it only
when research sources informed the agent, with slugs matching H2 headings in the package root's
`sources.md`.
## Body
Follow the Body section of `references/contract.md`: role instruction, one job, and delegation
to installed skills instead of transcribed procedure.
## Before invoking `agent-audit`
- [ ] `name` kebab-case, matching the filename stem, unique in scope
- [ ] `description` written to `references/contract.md`
- [ ] Every frontmatter key present in the `apm-agent-allowlist` section — in particular no `tools`
- [ ] No `FILL IN:` placeholder and no `<!-- ... -->` template comment anywhere in the file
- [ ] System prompt body non-empty, and a read-only agent says so in prose as well as in
`disallowedTools`
Then return to the flow reference you came from.