audit · git:20260905.8dcf691 · 2026-09-05 · sha256 61c5da17bf0043a4
audit git:20260905.8dcf691A
Immutable. This exact content never changes and is served at /api/v1/blob/61c5da17bf0043a4.
--- name: audit description: "Repository-specific check that every skill sits directly under kit/skills/, carries its `hor-` prefix, and declares a `name:` equal to its own folder name, and that the six places the documents state a skill count agree with what is there — the layout rules the build aborts on plus the count, reported all at once with a non-zero exit for CI. Use before committing a new, renamed or moved skill. It reports only; it renames and moves nothing, it checks no frontmatter field other than `name:`, and it cannot check a sibling library's count." --- # Audit A skill's folder name is its `name:` and the folder name it is installed under — one string, three places. Consuming repositories install every skill of this library, and of its sibling libraries — `hora-skills-ort-core` (`hoc-`) and `hora-skills-ort-furo` (`hof-`) — side by side under `.claude/skills/`, so all of those names live in **one flat namespace**, and the source layout is what protects it: one level of skill folders directly under `kit/skills/`, the `hor-` prefix that no sibling library uses, and a filesystem that will not hold two folders of one name in one directory. That protection is only as real as the layout. A skill placed one directory deeper, a `name:` edited without renaming its folder, a folder renamed without editing `name:` — none of these look wrong in the file they occur in, and each breaks the one guarantee the whole scheme rests on. This audit makes the state of the layout visible from outside any single file. ## What is checked For every entry directly under `kit/skills/`: - **An entry under `kit/skills/` that is not a directory, or a directory with no `SKILL.md` directly inside it, is a failure.** Only skill folders belong there, and a folder with no `SKILL.md` installs nothing. - **A `SKILL.md` anywhere below a skill folder's own top level is a failure.** The one-level layout is what makes the folder name the skill name; a skill nested inside another has no name of its own. A skill folder's `references/` and `scripts/` subdirectories are its own files, never more skills. - **A folder name that is not `hor-` followed by 1–60 characters of `[a-z0-9-]` is a failure.** The prefix is what keeps a sibling library's skill from being published by this one: `hoc-` or `hof-` here would claim a name that is not this package's to publish, and it is the prefix, not the directory a folder sits in, that a consuming repository ever sees. The rest of the rule guards the path — the name is joined onto `dist/skills/` as a path segment, so a value carrying `/` or `..` would land the folder somewhere else entirely; 64 characters is the limit an installed skill name has to stay within, of which the prefix takes 4. A single case keeps two names from folding onto one folder on a case-insensitive filesystem (macOS, Windows default). - **A `SKILL.md` with no parsable `name:` is a failure**, and **a `name:` that differs from its folder name is a failure.** This last one is the check everything else rests on: the two are one string by convention, and only an enforced comparison keeps them one string in fact. And, once per run, the number the documents claim: - **A stated skill count that disagrees with what is under `kit/skills/` is a failure.** Six places restate the count as prose — the catalog heading and the README opening in both languages, and this package's own row of the package table in both — and each one is a fact about a directory, written by hand. When a skill is added, the count in all six moves or none of them does, and nothing but this check reports the difference. The package table's other rows are **not** checked. They state a sibling library's count, which cannot be verified from inside this repository: that library is not here, and it has no way to announce that it grew. Those rows have gone stale before, and this audit cannot be what catches it. Duplicate names are not checked, because they cannot occur. Two skills of this library would need two folders of one name in one directory, and a sibling library's skills carry a prefix of their own — so once each `name:` is confirmed equal to its folder name, uniqueness follows from the filesystem rather than from a comparison. Nothing else is checked here. Description length, quoting, and the rest of how a `SKILL.md` is written belong to the skill-writing convention, which owns them. The walk starts at `kit/skills/` and goes nowhere else. This repository's own skills under `.claude/skills/` — this audit and the build — are tooling for maintaining the library, not library content: they are never installed into a consuming repository, so they never enter the flat namespace being protected here, and none of the rules above apply to them. That is why they are free to carry an unprefixed one-word name of their own. These are exactly the failures the build aborts on, and they are stated identically in both places on purpose. The build enforces them because a violation would destroy or misplace its output; this audit enforces them so a CI job can reject the commit before anyone runs a build. Neither is a substitute for the other, so the rule is written out in both rather than shared through an import that would tie one skill's script to the other's — and a test pins the two copies of the name pattern to each other, so changing it in one place alone fails. Every problem found is reported, grouped by kind, in one run: fixing a layout mistake often surfaces the next one, and a report that stops at the first failure would take as many runs as there are mistakes. ## Running ``` npm run skill-names ``` Or directly: ``` node .claude/skills/audit/scripts/audit.js ``` It exits `0` when every skill sits directly under `kit/skills/` and declares a `name:` equal to its folder name, and `1` on any of the failures above, so it can gate a commit or a CI job. ## Resolving a mismatch For a `name:` that disagrees with its folder name, decide which of the two is the name the skill should ship under, then make the other match it. The folder name is what a consuming repository installs and invokes, so the choice is about the installed command name, not about tidiness in the tree. For a skill in the wrong place — nested too deep, or carrying a prefix this library does not own — move the folder to the library its prefix names, or change both the prefix and `name:` to `hor-`. Renaming is the deliberate part: an installed skill's name is what consuming repositories invoke, so a rename is a breaking change for them. Renaming a skill breaks no references inside this library: the skill-writing convention prohibits referring to another skill by name or by relative path, requiring a descriptive reference to the concept instead. That prohibition exists so a rename stays local to the one folder.