Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 21 additions & 15 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,23 @@ layers, and `scripts/check-skill-spec` enforces the split. Anything outside both
which reject an unknown key outright rather than ignoring it. Three carry rules worth stating:

- `description`: what the skill does **and when to use it**, third person. The repo ceiling is
320 characters and its skills average 173, because the same string is catalogue copy in four
places: `/plugin`, `marketplace.json`, the plugin `README.md` blockquote and the skill's own
frontmatter. All four are compared; the blockquote is line-wrapped, so an edit by exact
string match reaches three of them and leaves the fourth stale.
320 characters and its skills average 204 — up from 173 once every one of them gained a
negative boundary clause, which is the trade that bought fewer misfires. The same string is
catalogue copy in four places: `/plugin`, `marketplace.json`, the plugin `README.md`
blockquote and the skill's own frontmatter. All four are compared; the blockquote is
line-wrapped, so an edit by exact string match reaches three of them and leaves the fourth
stale.
- `allowed-tools`: a **YAML list**, the one place this repo knowingly departs from the
standard's space-separated string. 70 grants contain a space (`Bash(git status:*)`), and the
space-free spelling `Bash(git:*)` widens the grant from one subcommand to all of git. Of the
two remaining spellings, a comma-separated string is split on spaces into patterns that
match nothing — a permission dropped in silence — while a list at worst reaches a consumer
that cannot use it as given. The field is still typed as a space-separated string and still
marked Experimental, so it is the one to leave. Note the claim this rests on has narrowed:
as of September 2026 tools are normalising a list into a string rather than rejecting it
loudly, which is the quiet handling the list was chosen to avoid. `CLAUDE_CODE_EXTENSIONS`
in `scripts/check-skill-spec` records what would make this decision worth revisiting.
standard's space-separated string. 79 of the 173 grants contain a space
(`Bash(git status:*)`), and the space-free spelling `Bash(git:*)` widens the grant from one
subcommand to all of git. Of the two remaining spellings, a comma-separated string is split
on spaces into patterns that match nothing — a permission dropped in silence — while a list
at worst reaches a consumer that cannot use it as given. The field is still typed as a
space-separated string and still marked Experimental, so it is the one to leave. Note the
claim this rests on has narrowed: as of September 2026 tools are normalising a list into a
string rather than rejecting it loudly, which is the quiet handling the list was chosen to
avoid. `CLAUDE_CODE_EXTENSIONS` in `scripts/check-skill-spec` records what would make this
decision worth revisiting.
- `metadata`: string keys to string values only — hence `version: "2.0.0"`, quoted.

**Claude Code extensions adopted here** — each costs portability, so each records its reason
Expand Down Expand Up @@ -76,8 +79,11 @@ Whether a procedure moves is a second, orthogonal question: unconditional or con
procedure every run needs is read anyway, one fetch later, so moving it costs a round trip and
buys nothing. A procedure only some runs need is what `references/` is for — put it there and
name the file where the condition is stated, because nothing in `references/` is read unless
the body says to read it. `agent-smith`'s Phase 2.5 is the example: it now runs only when the
plan touches a user-visible surface, so on most dispatches its detail is cost with no reader.
the body says to read it. `agent-smith`'s Phase 5 is the example, and so far the only one: it
runs in Autonomous mode alone, so its scoring table and iteration log sit in
`references/iteration.md` and a Partner-mode dispatch never pays for them. Phase 2.5 is
conditional too and still does not qualify — at eight lines, most of them the condition
itself, the fetch would cost more than the lines it saves.

### Skill Discovery (Three Tiers)

Expand Down
26 changes: 14 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,23 +225,25 @@ runs need is what `references/` is for. Put it there, and name the file at the p
condition is stated: loading is just-in-time, so nothing in `references/` is read unless the
body says to read it.

`agent-smith`'s Phase 2.5 is the example. It now runs only when the plan changes a
user-visible surface, so on every dispatch that does not, its detail is context paid for with
no reader. That is the shape `references/` exists for, and the reason this repo's own
machinery for it — resolved links, a required `## Contents` past 100 lines, no nesting — has
so far gone unused.
`agent-smith`'s Phase 5 is the example. It runs in Autonomous mode only, so its scoring table
and iteration log sit in `references/iteration.md`, and a Partner-mode dispatch never pays for
them. That is the shape `references/` exists for, and this repo's own machinery for it —
resolved links, a required `## Contents` past 100 lines, no nesting — has exactly that one
user so far. Phase 2.5 is conditional too and still does not qualify: at eight lines, most of
them the condition itself, the fetch would cost more than the lines it saves.

### Trigger evals

A skill only helps if it activates, and the `description` is the only thing Claude has at the
moment it decides. It is also the only part of a skill loaded on every session, whether or
not the skill ever fires, so a description here is kept near 160 characters: long enough to
say what the skill does and when to use it, short enough that all 19 together cost a session
under 800 tokens. Every plugin carries `evals/trigger-queries.json`: 20 labelled prompts, ten
that should activate the skill and ten near-misses that share its vocabulary but need something
else. Split 60/40 into train and validation, so a reworded description is tuned on one half and
judged on the other rather than fitted to the phrasings used to tune it. `scripts/validate`
fails a plugin that carries no such file.
not the skill ever fires, so a description here stays well under the 320-character ceiling:
they average 204 — long enough to say what the skill does, when to use it and what it is
_not_ for, short enough that all 19 together cost a session about 1,000 tokens. Every plugin
carries `evals/trigger-queries.json`: 20 labelled prompts, ten that should activate the skill
and ten near-misses that share its vocabulary but need something else. Split 60/40 into train
and validation, so a reworded description is tuned on one half and judged on the other rather
than fitted to the phrasings used to tune it. `scripts/validate` fails a plugin that carries
no such file.

The scrum agents carry one too. They used to be exempt on the grounds that `agent-smith`
dispatches them rather than a prompt triggering them, and that each is the next one's nearest
Expand Down
Loading