Two skill naming rules: verb-object, and family-role
Context and Problem Statement
A skill’s folder name is not a label on the folder — it is the identifier, six times over: the
name: frontmatter, the /ai-migration-kit:<name> invocation a user types, the
evals/<name>-trigger-eval.json filename, an entry in each of two literal Python rosters
(evals/run_all.py’s SKILLS and evals/trigger_eval.py’s DEFAULT_KNOWN), a row in the
per-skill area: taxonomy that auto-dev slices worker isolation from, and the string every
cross-referencing SKILL.md spells by hand. There is no indirection anywhere;
tests/skills/check-frontmatter.py turns the coincidence into a contract by refusing a name:
that differs from its folder and by asserting both rosters equal the skills/*/ folder set (#331).
The ten shipped skills were named three different ways at once. Six read as verb-object
(create-issue, implement-issue, merge-pr, triage-backlog, setup-repo, auto-dev); the
family members read as <family>-<role> (auto-dev-worker, auto-dev-merge; migrate,
migrate-assess, migrate-audit, migrate-verify, migrate-followups). Four did neither:
get-repo-profile was verb-object-object with a redundant get-; followups was a bare noun that
collided in grep with scripts/followups.py and commands/migrate-followups.md;
systematic-debugging named a style rather than a job; and legacy-upgrade was inverted and, worse,
was the head of the migrate-* family that all four of its own commands are named after, so a
contributor grepping migrate for the pipeline found the commands and the four siblings and missed
the head. Because the name is the identifier and not a label, that inconsistency was not contained
in one place: measured on main it spanned 53 files for legacy-upgrade, 49 each for
get-repo-profile and followups, 19 for systematic-debugging. Two of the four also made the
area: taxonomy contradict itself at the point where the isolation contract is read — area: migrate
covered a folder called legacy-upgrade, area: repo-setup one called get-repo-profile.
Considered Options
- Do nothing, and document the inconsistency in
ARCHITECTURE.md. - Rename, and ship alias folders (or a name-map) so the old invocations keep resolving.
- Rename only
legacy-upgrade— the one whose name actively misleads — and leave the other three. - Rename all four, with no aliases, and record the naming rule itself.
Decision Outcome
Every folder under skills/ is named by one of exactly two rules — (1) a standalone skill is
verb-object; (2) a member of a family is <family>-<role>, where <family> is itself a rule-1
name or the bare verb that heads the family — and the four non-compliant folders are renamed in the
2.0 breaking window with no aliases, shims, redirect folders or deprecation period
(get-repo-profile → profile-repo, followups → review-followups, systematic-debugging →
debug-issue, legacy-upgrade → migrate-legacy). Aliases lose on their own mechanics rather than
on taste: check-frontmatter.py demands one evals/<name>-trigger-eval.json set and two roster
entries per skills/*/ folder, so each alias would have to carry a duplicate eval set, and the
trigger bench would then score two descriptions competing for the same queries — measurably worse
than the inconsistency it papers over, and an alias is a second name for the same skill, which is
the exact thing the rules exist to end.
Consequences
Renames happen only in a major. A skill name is a user-typed invocation and a filename in five machine-read places, so changing one is a breaking change; outside a major nobody schedules it on its own, which is how four offenders accumulated. This record makes the wait affordable rather than arbitrary: a future rule-breaking name is fixed at the next major, not argued about again from scratch, and the ADR is what a reviewer cites when a new skill is proposed under a third shape.
The rules also make the ten-skill list predictable rather than arbitrary. README.md and
ARCHITECTURE.md now state the two rules and point here instead of explaining why four entries look
different, and a reader who knows the family (migrate-*, auto-dev-*) can predict the folder name
without opening skills/.
The cost is paid once, in this release, and it is real: every old invocation in a user’s muscle
memory, notes or scripts stops resolving, with no error that suggests the new name. It is also
paid inside the repo, since the name is spelled by hand in ~120 files — which is precisely the
argument for making the sweep once, under one rule, rather than four separate times. Because the
descriptions themselves are unchanged, a shift in the trigger bench after this lands is measurement
noise rather than a regression, and is re-run before it is acted on (ADR-0007 keeps
evals/run_all.py out of the worker fleet, so the owner runs it).