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 12 additions and 10 deletions
Showing only changes of commit c7c9311d80 - Show all commits

View File

@@ -36,7 +36,7 @@ Fall back to raw shell only when no skill covers it.
- **Do not add repo-owned keys to `.claude/settings.json`.** apm treats it as its own deployed artifact and `apm audit --ci` replays the install and diffs, so anything apm would not have written is permanent drift that fails the `apm-audit-ci` pre-push hook. A hook you want here is authored in `plugins/<name>/.apm/hooks/` and deployed by apm, never hand-written into that file. The `SessionStart` entry already in it is exactly that: kyberforge authors it in `plugins/kyberforge/.apm/hooks/hooks.json` and apm merges it in, so it is apm's own output, it is what the replay expects, and it belongs in the commit — do not strip it (ADR-0019). Machine-specific settings go in the gitignored `.claude/settings.local.json`; shared enforcement goes in `.pre-commit-config.yaml`. - **Do not add repo-owned keys to `.claude/settings.json`.** apm treats it as its own deployed artifact and `apm audit --ci` replays the install and diffs, so anything apm would not have written is permanent drift that fails the `apm-audit-ci` pre-push hook. A hook you want here is authored in `plugins/<name>/.apm/hooks/` and deployed by apm, never hand-written into that file. The `SessionStart` entry already in it is exactly that: kyberforge authors it in `plugins/kyberforge/.apm/hooks/hooks.json` and apm merges it in, so it is apm's own output, it is what the replay expects, and it belongs in the commit — do not strip it (ADR-0019). Machine-specific settings go in the gitignored `.claude/settings.local.json`; shared enforcement goes in `.pre-commit-config.yaml`.
- **`apm.lock.yaml` turning up modified is expected, not a bug.** kyberforge's `SessionStart` hook runs `apm outdated` at startup and `apm update --yes` when something is behind, which rewrites the lock. Commit or discard it deliberately. - **`apm.lock.yaml` turning up modified is expected, not a bug.** kyberforge's `SessionStart` hook runs `apm outdated` at startup and `apm update --yes` when something is behind, which rewrites the lock. Commit or discard it deliberately.
- **A `.apm/` edit is not live in this session until it is pushed.** The six dependencies resolve from the holocron remote, unpinned against the default branch. `apm install` deploys from the lock; `apm update` is what re-resolves refs. - **A `.apm/` edit is not live in this session until it is pushed.** The six dependencies resolve from the holocron remote, unpinned against the default branch. `apm install` deploys from the lock; `apm update` is what re-resolves refs.
- **The ADR-0020 skill gates ship hot, with no baseline — and the corpus is now clean.** All 39 skills clear both FAIL tiers: no description over 400 characters, no body over 900 words (counted body-only). Issue #99 retrofitted them plugin by plugin and `kyberforge` was the last wave. Because nothing is grandfathered, the gates now bite on first commit — a new skill, or an edit that pushes a description past 400, is blocked until it complies. **No routing target dangles**, and `tests/test-adr0020-targets.sh` pins that set as empty, so a new boundary clause naming a non-existent skill fails the suite rather than joining a backlog. Two blind spots survive: `skill-size-check` does not cover the Vale half, so `Kyberforge.CompositionNote` fires nowhere today but any new description can reintroduce it; and the `Kyberforge` style is scoped `[**/SKILL.md]`, so every `references/` file is unlinted — which matters because the contract's own remedy is to move prose *into* `references/`, out of the prose gate's reach. Check both: `pre-commit run --all-files`. - **The ADR-0020 skill gates ship hot, with no baseline — and the corpus is now clean.** All 39 skills clear both FAIL tiers: no description over 400 characters, no body over 900 words (counted body-only). Issue #99 retrofitted them plugin by plugin — `kyberforge` was the last plugin wave, followed by corpus-wide passes and two rounds of independent audit fixes. Because nothing is grandfathered, the gates now bite on first commit — a new skill, or an edit that pushes a description past 400, is blocked until it complies. **No routing target dangles**, and `tests/test-adr0020-targets.sh` pins that set as empty, so a new boundary clause naming a non-existent skill fails the suite rather than joining a backlog. Two blind spots survive: `skill-size-check` does not cover the Vale half, so `Kyberforge.CompositionNote` fires nowhere today but any new description can reintroduce it; and every `references/` file is unlinted — which matters because the contract's own remedy is to move prose *into* `references/`, out of the prose gate's reach. That blind spot has **two** independent causes and closing either alone changes nothing: the `Kyberforge` style is scoped `[**/SKILL.md]`, *and* the `vale-audit-prefilter-skill` hook filters on `files: '^plugins/[^/]+/\.apm/skills/[^/]+/SKILL\.md$'`, so a reference file is never handed to Vale whatever the style says (#117). Check both gates: `pre-commit run --all-files`.
- **Run `bash tests/run-tests.sh --strict` before considering any change done.** Keep the flag: without it a suite whose dependency is missing exits 77 and is counted SKIPPED rather than failed, so the run goes green having verified less than it claims. - **Run `bash tests/run-tests.sh --strict` before considering any change done.** Keep the flag: without it a suite whose dependency is missing exits 77 and is counted SKIPPED rather than failed, so the run goes green having verified less than it claims.
- **Before pushing, rehearse the gate locally:** `pre-commit run --hook-stage pre-push --all-files`. It runs the 14 pre-push hooks this repo authors itself plus pre-commit's 2 `meta` hooks, so it prints 16; `check-release-needed` passes without checking anything, because it needs a real push to `main`. `docs/spec/gates.md` reconciles both. - **Before pushing, rehearse the gate locally:** `pre-commit run --hook-stage pre-push --all-files`. It runs the 14 pre-push hooks this repo authors itself plus pre-commit's 2 `meta` hooks, so it prints 16; `check-release-needed` passes without checking anything, because it needs a real push to `main`. `docs/spec/gates.md` reconciles both.
- **Pushing without a network** needs `SKIP=apm-marketplace-check,apm-pack-check-clean git push` — those two resolve a remote marketplace entry via `git ls-remote`. Skip only those two; the rest are real local checks, and adding one to `SKIP` disarms it silently. - **Pushing without a network** needs `SKIP=apm-marketplace-check,apm-pack-check-clean git push` — those two resolve a remote marketplace entry via `git ls-remote`. Skip only those two; the rest are real local checks, and adding one to `SKIP` disarms it silently.

View File

@@ -282,7 +282,7 @@ bash scripts/skill-size-check.sh plugins/*/.apm/agents/*.agent.md
``` ```
exits 1 today with 900-word body FAILs on `git-orchestrate` (933), `gitea-orchestrate` (1,199) and exits 1 today with 900-word body FAILs on `git-orchestrate` (933), `gitea-orchestrate` (1,199) and
`apm-orchestrate` (1,080). Agent files escape only because the hook definitions filter on `SKILL.md` `apm-orchestrate` (1,113). Agent files escape only because the hook definitions filter on `SKILL.md`
— a file-pattern accident that happens to implement the design, not the design itself. **Do not — a file-pattern accident that happens to implement the design, not the design itself. **Do not
"extend" that hook's `files:` pattern to cover agents** on the assumption that the script already "extend" that hook's `files:` pattern to cover agents** on the assumption that the script already
knows the difference; doing so silently enforces a gate ADR-0020 declines to set. knows the difference; doing so silently enforces a gate ADR-0020 declines to set.
@@ -292,20 +292,22 @@ knows the difference; doing so silently enforces a gate ADR-0020 declines to set
**The ADR-0020 gates ship hot, with no baseline file.** A shrinking baseline recording each **The ADR-0020 gates ship hot, with no baseline file.** A shrinking baseline recording each
non-compliant skill's current numbers was considered and rejected in favour of hot gates. non-compliant skill's current numbers was considered and rejected in favour of hot gates.
Two independent hot gates are currently red, and the first will not warn you about the second. **The corpus is now clean on both gates.** Issue **#99** retrofitted all 39 skills plugin by plugin;
`kyberforge` was the last wave, followed by two corpus-wide passes.
| Gate | Current findings | | Gate | Current findings |
|---|---| |---|---|
| `skill-size-check` | **26 of 39** descriptions and **9 of 39** bodies exceed their FAIL tier; 2 dangling targets; 58 SUGGESTIONs | | `skill-size-check` | **0 of 39** descriptions and **0 of 39** bodies exceed their FAIL tier; 0 dangling targets; 31 SUGGESTIONs |
| `Kyberforge.CompositionNote` (Vale) | **10 errors across four skills**: `gitea-issues`, `gitea-labels-milestones`, `gitea-prs`, `gitea-workflow` | | `Kyberforge.CompositionNote` (Vale) | **0 errors** — the four `gitea-*` carriers were all retrofitted |
`Kyberforge.CompositionNote` is the ADR-0020 Vale rule banning composition and architecture prose `Kyberforge.CompositionNote` is the ADR-0020 Vale rule banning composition and architecture prose
from a description. Every Vale rule here is `level: error` with no ignorable tier, so touching any of from a description. Every Vale rule here is `level: error` with no ignorable tier, so a description
those four skills means fixing its prose findings as well as its size findings. that reintroduces one blocks the commit even though no skill carries one today.
Consequence: editing a non-compliant skill *for any reason* means retrofitting it to the contract Because nothing is grandfathered, the gates now bite on **first commit**: a new skill, or an edit
first — a one-line fix to `gitea-prs` cannot be committed until that skill complies. This is that pushes a description past 400 characters or a body past 900 words, is blocked until it
deliberate; it guarantees convergence and avoids a half-state. Tracked as Gitea issue **#99**. complies. That is the steady state the retrofit was for — it is no longer true that an unrelated
one-line fix to a skill requires retrofitting that skill first.
Check where a skill stands before starting, and check **both** gates: Check where a skill stands before starting, and check **both** gates: