refactor(skills): retrofit the corpus to the ADR-0020 context contract #129

Merged
Defame1297 merged 89 commits from refactor/adr0020-skill-retrofit into main 2026-09-01 13:47:47 +00:00
2 changed files with 33 additions and 12 deletions
Showing only changes of commit ccc54cbb58 - Show all commits

View File

@@ -16,8 +16,12 @@ decisions.
**Preload tax**: **Preload tax**:
The always-on context cost of every installed skill's `name` and `description`, charged from the The always-on context cost of every installed skill's `name` and `description`, charged from the
first token of every session whether the skill is invoked or not. Measurement method and current first token of every session whether the skill is invoked or not. Measurement method: ADR-0020. Its
figure: ADR-0020. **23,427 characters is the pre-retrofit baseline, not a current reading** — measured at the decision
commit, before #99. Across the same 39 skills it is **10,478 characters** (~2,620 tokens) as of
2026-09-01. Both figures move with the corpus, so re-derive rather than quote either: sum
`len(name) + len(description)` over the frontmatter of every `plugins/*/.apm/skills/*/SKILL.md`,
folding block scalars as `scripts/skill-size-check.sh` does.
_Avoid_: context cost, token overhead _Avoid_: context cost, token overhead
**Skill context contract**: **Skill context contract**:
@@ -44,7 +48,7 @@ _Avoid_: router body, thin body
A skill reached only by typing its slash command, declared `disable-model-invocation: true`. The host A skill reached only by typing its slash command, declared `disable-model-invocation: true`. The host
withholds it from the model-visible listing entirely, so it pays no preload tax and its description withholds it from the model-visible listing entirely, so it pays no preload tax and its description
becomes human-facing text. The flag also hard-blocks the Skill tool, so **no other skill can route to becomes human-facing text. The flag also hard-blocks the Skill tool, so **no other skill can route to
a hand-invoked skill** — a `Call \`x\`` step in another skill's body stops working the moment `x` a hand-invoked skill** — a `` Call `x` `` step in another skill's body stops working the moment `x`
takes the flag. Check inbound routes before declaring one. Exemplar: `zoom-out`. takes the flag. Check inbound routes before declaring one. Exemplar: `zoom-out`.
_Avoid_: manual skill, disabled skill _Avoid_: manual skill, disabled skill

View File

@@ -127,8 +127,15 @@ clause**, and a **boundary clause**. Capability enumeration, output-format detai
gate shipping hot with no baseline cannot give two answers. Under the walk-up those four resolve gate shipping hot with no baseline cannot give two answers. Under the walk-up those four resolve
because sibling plugins are in the universe — no plugin here declares a cross-plugin apm because sibling plugins are in the universe — no plugin here declares a cross-plugin apm
dependency, and none needs to. Verified: a tree holding only `plugins/` and the root `apm.yml`, dependency, and none needs to. Verified: a tree holding only `plugins/` and the root `apm.yml`,
with no `.claude/` or `.agents/` anywhere, now produces findings identical to the working tree — with no `.claude/` or `.agents/` anywhere, produced findings identical to the working tree. The
26 description FAILs, 9 body FAILs, 2 dangling targets, 0 missing references, 58 SUGGESTIONs. figures that reproduction recorded — 26 description FAILs, 9 body FAILs, 2 dangling targets, 0
missing references, 58 SUGGESTIONs — are the **pre-retrofit** corpus as it stood when the
experiment ran, kept here as the evidence for the install-independence claim, not as a current
reading. *Amended 2026-09-01: the #99 retrofit took the first three to zero. Measured at that
date over the same install-free tree: 0 description FAILs, 0 body FAILs, 0 dangling targets, 0
missing references, 29 SUGGESTIONs.* What the experiment establishes is that the two trees agree,
not what either measured; re-derive rather than quote —
`bash scripts/skill-size-check.sh plugins/*/.apm/skills/*/SKILL.md`.
- **The universe is the apm marketplace, and nothing else.** A routing target resolves to a skill or - **The universe is the apm marketplace, and nothing else.** A routing target resolves to a skill or
an agent, or it does not resolve. Host built-ins are deliberately outside it: `/compact`, `/clear` an agent, or it does not resolve. Host built-ins are deliberately outside it: `/compact`, `/clear`
and `/init` are Claude Code slash commands with no counterpart in Copilot CLI or Codex, so a and `/init` are Claude Code slash commands with no counterpart in Copilot CLI or Codex, so a
@@ -185,8 +192,11 @@ becomes the system prompt of a fresh context. The rationale for the 900-word FAI
That exemption is expressed in `agent-audit/scripts/validate.sh`, which has no body constant, and in That exemption is expressed in `agent-audit/scripts/validate.sh`, which has no body constant, and in
the `files:` pattern of the `skill-size-check` pre-commit hook, which is `SKILL.md`-only. It is *not* the `files:` pattern of the `skill-size-check` pre-commit hook, which is `SKILL.md`-only. It is *not*
expressed in `scripts/skill-size-check.sh` itself, which measures whatever path it is handed — expressed in `scripts/skill-size-check.sh` itself, which measures whatever path it is handed —
running it directly over `plugins/*/.apm/agents/*.agent.md` today reports 900-word body FAILs on running it directly over `plugins/*/.apm/agents/*.agent.md` exits 1 with 900-word body FAILs on
`git-orchestrate` (933), `gitea-orchestrate` (1,199) and `apm-orchestrate` (1,080). Agents escape by `git-orchestrate` and `gitea-orchestrate`. *Amended 2026-09-01: this sentence named a third agent,
`apm-orchestrate`, at 1,080 words. It is 876 today — a SUGGESTION, not a FAIL. Counts are
deliberately no longer pinned here: agent bodies are edited like any other file and a figure in this
paragraph goes stale the moment one is trimmed. Run the command.* Agents escape by
file pattern, not by the script knowing the difference. Anyone widening that pattern to cover agents file pattern, not by the script knowing the difference. Anyone widening that pattern to cover agents
would silently enforce a gate this ADR declines to set. would silently enforce a gate this ADR declines to set.
@@ -245,7 +255,7 @@ which tier each rule is in, because the failure this ADR is most exposed to is a
| description characters (250 SUGGESTION † / 400 FAIL) | skills, agents | deterministic | `scripts/skill-size-check.sh`; constants mirrored in `skill-audit/scripts/validate.sh` and `agent-audit/scripts/validate.sh` | | description characters (250 SUGGESTION † / 400 FAIL) | skills, agents | deterministic | `scripts/skill-size-check.sh`; constants mirrored in `skill-audit/scripts/validate.sh` and `agent-audit/scripts/validate.sh` |
| body-only words (600 SUGGESTION / 900 FAIL) | skills | deterministic | `skill-size-check.sh`, `skill-audit/scripts/validate.sh` | | body-only words (600 SUGGESTION / 900 FAIL) | skills | deterministic | `skill-size-check.sh`, `skill-audit/scripts/validate.sh` |
| description present and non-empty (ERROR) | skills, agents | deterministic | same | | description present and non-empty (ERROR) | skills, agents | deterministic | same |
| boundary target resolves to a real skill or agent (ERROR when written in route notation — `/name`, or any arrow form; or when a *terminal* bare name's own sentence names another target that resolves; SUGGESTION otherwise) | skills, agents | deterministic | same | | boundary target resolves to a real skill or agent — **three** verdicts, not two (ERROR when written in route notation — `/name`, or any arrow form; or when a *terminal* bare name's own sentence names another target that resolves. SUGGESTION otherwise. INFO, "DID NOT RUN", exit 0, when no skill universe could be determined for the path at all — no authoring root above it, no apm package root, no declared apm dependencies, no deployed `.claude/` or `.agents/` tree: the targets are named and left unchecked) | skills, agents | deterministic | same |
| boundary clause absent — `absent` (SUGGESTION) † | skills, agents | deterministic | same | | boundary clause absent — `absent` (SUGGESTION) † | skills, agents | deterministic | same |
| an arrow clause is present but no target can be read out of it — `unparsed` (SUGGESTION) † | skills, agents | deterministic | same | | an arrow clause is present but no target can be read out of it — `unparsed` (SUGGESTION) † | skills, agents | deterministic | same |
| one arrow clause naming two or more targets, of which only the first is resolved (SUGGESTION, issue #107) † | skills, agents | deterministic | same | | one arrow clause naming two or more targets, of which only the first is resolved (SUGGESTION, issue #107) † | skills, agents | deterministic | same |
@@ -378,10 +388,17 @@ carries is the ordinary one for hot gates: a gate expensive enough to be inconve
with `SKIP=` and loses its authority. with `SKIP=` and loses its authority.
**A second hot gate ships alongside it, and it is easy to miss.** `Kyberforge.CompositionNote` is **A second hot gate ships alongside it, and it is easy to miss.** `Kyberforge.CompositionNote` is
`level: error` like every other rule in that style, so `pre-commit run --all-files` is red on 10 `level: error` like every other rule in that style, so at decision time `pre-commit run --all-files`
alerts across `gitea-issues`, `gitea-labels-milestones`, `gitea-prs` and `gitea-workflow` was red on 10 alerts across `gitea-issues`, `gitea-labels-milestones`, `gitea-prs` and
independently of anything `skill-size-check` reports. Someone scoping the #99 retrofit off the size `gitea-workflow` independently of anything `skill-size-check` reports. Someone scoping the #99
findings alone will fix those and still be blocked. The two gates want fixing together. retrofit off the size findings alone would have fixed those and still been blocked. The two gates
wanted fixing together, and were. *Amended 2026-09-01: that figure is historical. The Vale prefilter
over the same 39 files now reports 0 errors, 0 warnings and 0 suggestions, so
`Kyberforge.CompositionNote` fires nowhere in the corpus today. The rule is still hot and still
independent of `skill-size-check`, so a new description can reintroduce it; `skill-size-check` does
not cover the Vale half, and no `references/` file is linted by anything (`docs/spec/gates.md` has
both causes, issue #117 tracks them). Re-derive rather than quote —*
`bash plugins/kyberforge/.apm/skills/skill-audit/scripts/vale-wrap.sh plugins/*/.apm/skills/*/SKILL.md`.
**A ceiling does not produce an average.** If every author writes to the 400-character FAIL, the **A ceiling does not produce an average.** If every author writes to the 400-character FAIL, the
preload lands at 39 × 400 = 15,600 chars — a 33% cut off 23,427, not the ~50% intended. Writing to preload lands at 39 × 400 = 15,600 chars — a 33% cut off 23,427, not the ~50% intended. Writing to