the standard
Spec
A registry is only as good as what it refuses. This is what a skill has to be, and what a rule has to survive.
Skill format
What a skill file is, and which half of it reaches the model.
A skill is one directory: a SKILL.md, and optional references/.
skills/android/kotlin/
├── SKILL.md
└── references/
└── cancellation.md
Frontmatter
---
name: android/kotlin # family/skill, the installable identity
axis: language # core | language | framework | service | topic
family: android
description: "Kotlin mechanics on Android: coroutine scope and cancellation, flow
collection, nullability and equality. Use when most of the code is Kotlin." # quoted: a
# plain YAML scalar may not contain a colon followed by a space
requires: [android/core] # skills that must be installed alongside
version: 1.1.0 # semver; a retirement is a minor bump
authors: [ahmmedrejowan]
agent_sections: [rules] # which sections an installer gives the agent
retired: [ASYNC-1, TYPE-2] # rule ids removed, and why, in Provenance
detect: # how a project is recognised
gradle_dependency: ["org.jetbrains.kotlin:kotlin-stdlib"]
---
description is one line, and it is read by both audiences. A person scanning the registry
decides from it whether to open the skill; a model deciding whether a skill is relevant reads the
same sentence. Say what the skill governs, then when it applies. It is metadata, so adding or
rewording one is a patch.
agent_sections is the load-bearing field. The file is written for two readers. An installer
gives the agent only the listed sections; everything else, ## Why, ## Pitfalls
## Provenance, is for the human deciding whether to trust the skill. That split is what lets a
skill carry its own evidence without spending the agent's context on it.
Versioning
Every skill starts at 1.0.0 when it is first published here. Work before publication happened
in a private workspace and its version numbers were internal bookkeeping, not a release history, so
they are not carried over.
After publication, semver applies to what an installer receives:
| change | bump |
|---|---|
| a rule retired, added, or its instruction changed | minor |
wording, Why, Pitfalls, Provenance, references |
patch |
a rule id renamed, or requires changed |
major |
detect widened or narrowed |
minor |
Evidence is anchored to evals, not to versions, because the evals are published and the
pre-publication versions were not. A ## Provenance entry says "eval 19 measured all four", never
"v1.3.0 added".
detect keys are family-defined and resolved by the index. Current keys: gradle_dependency
gradle_plugin, file, manifest_element, manifest_attribute.
Sections
| section | audience | required |
|---|---|---|
## Rules |
the agent | yes |
## Why |
the human | yes for a shipped skill |
## Pitfalls |
the human | yes for a shipped skill |
## Provenance |
the human | yes for a shipped skill |
A rule
Three parts, always:
- **ASYNC-4** `must`: A read that can be re-triggered, a query, a filter, a refresh, is
cancelled by the operator built for it, not by tracking jobs by hand.
*Why:* hand-tracked jobs race their own cancellation on fast input. And cancelling a write does
not un-send it, the request may already have reached the server.
*Not when:* every emission must be processed, where the operator is losing work by design.
A decidable instruction. Not "use flows appropriately", a reader must be able to look at a file and say yes or no. Undecidable text is unfalsifiable, and unfalsifiable text is unactionable.
A Why that names the failure not the principle. The reader has to be able to recognise the
bug in the wild.
A Not when. Almost nobody writes these, and their absence is what turns a rules file into a
nitpick generator. A rule without a stated boundary gets applied everywhere.
Rule ids
PREFIX-N, stable for the life of the skill. Ids are never reused. A retired rule's id is
listed in retired: and stays retired, because published evidence and cross-references point at it.
Priority
must, the failure is silent, expensive, or hard to reverse.
should, real exceptions exist, and the rule says to name yours.
Cross-references
A rule may cite another skill's rule as `android/core WORK-3`. Every such reference is
resolved by scripts/xrefcheck.py in CI; a reference to a rule that does not exist fails the
build. Layers must not restate each other, see METHOD.md.
The axes
Five of them. A rule belongs to exactly one.
A skill belongs to exactly one axis. The axis determines when it loads.
| axis | loads when | examples |
|---|---|---|
| core | always, for the family | android/core |
| language | the language is present | android/kotlin, android/java |
| framework | the UI toolkit or framework is present | android/compose, android/xml |
| service | an external service is used | appwrite/core (not yet built) |
| topic | a concern only some projects have | android/db, android/permissions |
A project loads the intersection: its core, its language, its framework, whichever services it calls, and whichever topics it actually has.
The core / topic boundary
Core is what every project in the family has. A topic is what only some have which is exactly what makes a topic detectable.
If a rule would fire on every project, it belongs in core and needs no detection. If it fires on
some, it is a topic and the detect block is what decides.
Topics group by concern, not by library
android/db covers persistence, what is stored, what survives, what is encrypted at rest, whether the project uses Room, DataStore, or files. It is not android/room.
The test: does this say something the library's own documentation does not? A skill organised around a library tends to restate that library's docs, which is the material the model already has.
What is not an axis
Architecture patterns (mvi, clean) are not skills: nothing detects them. They resolve to a set
of skills, core + kotlin + compose, and are better expressed as a stack template.
A concern with no detection signal is a references/ file inside the skill that owns it, not a
skill of its own.
Family
A family is a platform or ecosystem: android, web, appwrite. Families compose sideways, a
project can load android/* and appwrite/* together, which is why service is its own axis
rather than a topic inside each platform.
Admission
The four tests a rule has to pass to be published.
A rule ships only if it passes all four tests, in order.
1. Does the model get this wrong unprompted?
The first test, and the one that outranks correctness. A rule the model already follows costs context and displaces one that would have worked.
This is decided by running the task with no skill loaded and reading what comes out. Not by judgement, and not by how important the rule feels.
Rules have been dropped against this test, including ones that felt obviously right. The in-context permission-timing rule is the most-repeated advice in the permissions corpus, and no control run of twelve got it wrong.
2. Does a linter or the compiler already catch it?
Most of a style guide is already a compiler error, a lint warning, or something the formatter fixes. A published survey of agent context files found 62% carrying rules a linter already enforces occupying context for nothing.
3. Would 900 of 1000 projects hit it?
Not 1 in 1000. Interesting is not the bar.
How not to score this test: by asking whether the situation is possible. Almost anything is possible. Ask whether a project that never thinks about this ships the bug, and whether the person writing the code would recognise it.
4. Is it checkable at every token, or a deferred action?
Constraints that describe a property of the artifact hold under load. Constraints that defer an action to a later moment drop sharply, while fully visible in context the whole time.
GOOD (a property of the artifact)
Money is a 64-bit integer of minor units plus a currency code, never a float.
BAD (an action deferred to later)
Remember to add a migration when you change the schema.
Both are true. Only the first is a rule. No skill in this registry carries a checklist because a deferred action stated as a rule is dropped at exactly the moment it matters.
A rule lives in a capability window
A rule earns its slot between two bounds. Above the window the model already does it; below the window the model cannot act on it even when told.
Both bounds are real and measured:
| rule | Opus | Sonnet | Haiku |
|---|---|---|---|
core STATE-1 save state for process death |
6/6 unaided | 0/2 → 2/2 | 0/2 → 2/2 |
core STATE-4 input owned by the control |
0/6 → 6/6 | 0/2 → 1/2 | 0/2 → 0/2 |
STATE-1 is the rule that curating against the frontier alone would have deleted: the strongest
model does not need it, and two of three do. STATE-4 is the inverse, it separates only on the
model capable of acting on it.
So a rule is not admitted or retired globally. It is admitted for a window, and the registry records which models were tested.
Corpus support is an anti-signal
The material a rule could be drawn from and the material the model was trained on are the same material. The more repositories document a practice, the more likely the model already follows it.
Measured repeatedly, including within a single skill: the rules with zero corpus support were the entire measured effect, while the corpus-backed rules in the same file were satisfied by every arm. Across six axes, register size has predicted result thinness rather than richness, the largest register in the project (238 claims) produced the smallest skill (3 rules).
Every confirmed result here came from a control arm or a primary source. None came from corpus frequency.
What the evidence covers
Every skill here was measured: drafted from a claim register, checked against primary sources, and
put in front of control and treated arms across four model families. What separated is recorded in
evidence/, skill by skill, including the results that went the other way.
An eval task is a spot check, not a proof. These tasks are small and self-contained so a run finishes in minutes and a difference is attributable. A real project is larger, has more context competing for the model's attention, and compounds a mistake across many files. A rule that moves a small task by one run moves a real codebase by considerably more, and a rule that a small task cannot tempt is not thereby unnecessary.
So the evidence is read as: this is what we could demonstrate under controlled conditions, and the effect in a real project is larger rather than smaller.