Harness Engineering / The Capability Ladder / the primitives

Four rungs, from a macro to a process.

Half a charter says what the agent may not do. The other half says what it can do at all, and that half is four primitives deep: a command, a skill, a sub-agent, an MCP server. They form one ladder, from a macro the model reads every word of to a process it can only call. This page is the mechanics of each rung, what makes a good one, and how to get a capability found once you have written it.

the argument · four primitives, one axis. How much of this can the model reinterpret
the other half · Constraint Engineering models what an agent may not do. This page models what it can
the sequel · Keeping Capabilities Honest covers the second job: what each capability costs on every turn, how to make one deterministic, and how to tell whether it ever fires
what it is not · a features tour. The primitives are stable and the placement rule is the part that travels
what is honest · I have measured constraint placement to a standard I trust and have not measured capability placement the same way. The sequel says what I would run
reads alongside · Anatomy of a Charter · Constraint Engineering · The Factory · Prompt Injection
license · CC BY 4.0. Take the ladder, not the vocabulary
Trigger
Who invokes it, and how they find it. The attribute a constraint never needs, because a constraint fires on its own.
Rung
What carries it, and how much of it the model can read and reinterpret. None, convention, context, or process.
Grant
What it may touch while it runs. Declared in a file, or inherited by accident.
Return
What crosses back to the caller, and what stays behind. The half of the interface people forget to design.
Part I

The family

What a capability is, the four attributes every one of them carries, and the two words this page prices everything in. One section, because the ladder starts in Part II.

0 The family test

Run the family test backwards and you have found a capability

A charter is easy to read as a list of prohibitions. Most of a working charter is not. It is the set of things the agent can do at all, and teams install those without designing them, because installing feels free. It is not free. A capability has the same four attributes a constraint has, and one more that constraints never need.

trigger who invokes it rung what carries it grant what it may touch return what crosses back

Constraint Engineering ends its section on component families with a ten-second test. Remove the component and ask what changed. Run that test and keep only the answers that point one way.

the family test, and the one answer this page followsremove it, then look
Remove it, andYou removed aWhere it is designed
the agent can do lesscapabilitythis page
the agent can do moreconstraintConstraint Engineering
a number movedsettingthe charter's config block
you know less about the runevidenceThe Audit Trail
the agent forgot somethingstatememory, and it needs an expiry

The five answers are exclusive in practice and not in theory. A skill that says always run the formatter reads like a capability and behaves like a rule. Removing it lets the agent do more, so it is a constraint, and it belongs in the charter at the rung the charter chose. That single misfiling is the most common one I see, and section 12 lists it as an anti-pattern rather than a style preference.

0.1 · Four attributes, and the fourth is the new one

A constraint carries a layer, a rung, a policy and a verdict. A capability carries four of its own. Three of them have obvious counterparts. The fourth does not exist on the constraint side at all, because a constraint does not need to be found.

the four attributes of one capabilitycollapse any two and section 12 has a name for it
AttributeThe question it answersConstraint counterpartDesigned in
triggerWho invokes this, and how do they find it?none. A constraint fires on its ownsection 9
rungWhat carries it, and how much of it can the model reinterpret?rung: reach and authoritysections 1–5
grantWhat may it touch while it runs?layer: whose rule it issection 10
returnWhat crosses back to the caller, and what stays behind?policy: what happens when it firesthe ledger, in the sequel

The missing counterpart is the whole reason these two pages exist. A hook runs because an event happened. A gate runs because the pipeline reached it. Neither one waits to be noticed. Every capability on the ladder below waits to be noticed, and three of the four wait to be noticed by a model reading a one-line description.

0.2 · Two prices, named once

Every rung below is priced twice on this page, so the two words are worth pinning here. The standing cost is what the capability puts in the context window on every turn, whether it fires or not: a name, a description, a schema. The invoked cost is what it puts there when it does fire: a body, a file, a result. One is paid thousands of times a week and the other is paid when the work needs it.

That is the whole reason the ladder has four rungs rather than one file format, and the accounting behind it belongs to the sequel rather than to this page. Keeping Capabilities Honest measures both prices, and the rest of this page just states them per rung and moves on.

The definition, so the rest of the page can be short

A capability is a component whose removal means the agent can do less. It is described by a trigger, a rung, a grant and a return. It charges context on every turn and pays out only when its trigger matches. Design all four attributes or you have installed a file and called it a design.

Part II

The four rungs

One ladder, from open to hidden. A command hides nothing and the model reads every word. A server hides everything but a schema. Each section states what the rung is, what it costs, what it buys, and the signal that you are standing on the wrong one. Section 5 handles the case where the answer is not a rung at all.

1 Command

A command is a macro, and its discovery problem belongs to a human

A command is a file of prompt text with a name. Somebody types the name and the text becomes the turn. Nothing is hidden, nothing is decided, and nothing fires unless a person makes it fire. That last property is the rung's whole advantage and its whole ceiling.

rung 0 open trigger a human types it boundary none
Standing cost
A name and one line. Zero when you turn model invocation off.
Invoked cost
The whole body, expanded in place, plus whatever it injects.
What returns
Nothing. The command is the turn.
Boundary
None. Every word is in the window and the model may rewrite the plan.

1.1 · What it actually is

A markdown file, in the project at .claude/commands/ or in the account at ~/.claude/commands/, with a subdirectory for a namespace. The frontmatter is small and every field is a lever on the ledger.

.claude/commands/charter-check.mda rehearsed prompt with the context stapled to it
---
description: Check the current diff against the charter's rung-1 rules
argument-hint: [base-branch]
allowed-tools: Bash(git diff:*)
disable-model-invocation: true
---

The diff under review, against $1:

!`git diff $1...HEAD --unified=0`

Read @docs/charter.md. Check the diff against the rung-1 section only.
Report one line per violation, and nothing else. Do not fix anything.

Three of those lines are the interesting ones. $1 and $ARGUMENTS make the file a function rather than a note. The ! prefix runs a shell command when the command expands and drops the output in, which means the prompt arrives already holding its evidence. disable-model-invocation takes the description out of the standing bill, which is the right default for anything a human always initiates.

1.2 · What the rung buys

  • Rehearsal. A prompt you got right once, replayed exactly. This is worth more than it sounds, because most bad agent turns start with a badly stated ask.
  • Evidence at the front. The ! injection collects the diff, the log, the failing test before the model starts. It cannot forget to look.
  • Near-zero standing cost. With model invocation off, a hundred commands cost you almost nothing on a turn that does not use them.
  • A human in the loop by construction. Somebody chose to run this. For anything irreversible that is a feature, and The Factory treats it as one of the places a human gets asked.

1.3 · The ceiling

A command cannot fire when the model needs it. It fires when a person remembers it. So the moment the value of the capability depends on it running at the right time rather than at a chosen time, this rung is wrong, and it is wrong in two different directions.

the two ways a command is the wrong rungboth are common
What you noticeWhat it meansWhere it goes
it must run every time, and someone forgotthis is not a capability. It is a rule with a typing requirementa hook, or a gate. Constraint Engineering
the model should have known to do thisthe trigger belongs to the model, not to youa skill. Section 2
you type it constantly with the same argumentsit is a default, not a capabilitythe charter, or a setting
The mistake worth naming once

A command that guards something that matters is a guardrail made of human memory. If the consequence of skipping it is a bad merge rather than a worse paragraph, it was never a capability. Promote it to a mechanism that fires on its own and keep the command as the convenient path, not as the enforcement.

2 Skill

A skill is a module, and its description is the only part that is always there

A skill is knowledge and procedure the model loads when it decides the job needs them. It is the first rung where the trigger belongs to the model, which makes one line of frontmatter the highest-leverage text in the whole capability. Everything else about the design follows from keeping the standing cost at that one line.

rung 1 loaded on demand trigger the model, from a description boundary convention
Standing cost
A name and one line, on every turn.
Invoked cost
The body when it triggers, then bundled files only if something reads them.
What returns
Every word, into this same window. Nothing is summarized.
Boundary
Convention. The model can read the file, argue with it, or ignore it.

2.1 · Three levels, and the design work is deciding what lives where

A skill is a directory with a SKILL.md and whatever it needs beside it. The three levels are not a packaging detail. They are the two prices of section 0, expressed as a file layout.

the three levels of a skillmove weight downward
LevelWhat it holdsWhen it is paid forBudget
descriptionwhen to use this, in the words a user would useevery turnone or two sentences. Treat it as expensive
SKILL.md bodythe procedure. The steps, the order, the refusalson triggershort enough to read in one pass
bundled filesreference tables, schemas, examples, scriptsonly if something reads or runs themas large as the job needs
.claude/skills/migration-writer/procedure in the body, reference in the files
migration-writer/
├── SKILL.md          the procedure. Around a page
├── references/
│   ├── column-types.md   the table nobody memorizes
│   └── rollback.md       read only when a rollback is asked for
└── scripts/
    └── check_reversible.py  deterministic. Run, do not read

--- SKILL.md ---
---
name: migration-writer
description: Write, review or roll back a database migration in this
  repository. Use when the user mentions a migration, a schema change, adding
  or dropping a column, an index, or a backfill. Covers the reversibility
  check this repository requires before a migration can merge.
allowed-tools: Read, Write, Edit, Bash(python scripts/check_reversible.py:*)
---

1. Read the existing migrations in db/migrate to match the local style.
2. Write the migration. Every one is reversible or it does not merge.
3. Run scripts/check_reversible.py against the new file. Do not
   reimplement the check by reading the file yourself.
4. For column types, read references/column-types.md. Do not guess.

The script in that layout is the part people skip, and it is the cheapest determinism on the page. A bundled script has no standing cost, no schema, no process to run, and no configuration. It answers the same way every time. Reach for an MCP server after you have established that a script cannot do it, not before.

2.2 · What the rung buys

  • Knowledge that arrives when relevant. This is the whole product. A reference library that costs one line until the moment it is needed.
  • Procedure with an order. A skill can say do this before that in a way a note in the charter cannot, because it arrives with the job rather than with the session.
  • Bundled determinism. Scripts beside the procedure, invoked by name, with a grant that permits exactly them.
  • A shared unit. Team-owned in the repository, account-owned for personal habits, or packaged in a plugin for distribution.

2.3 · The ceiling

Two limits, and they are different in kind. The first is that the boundary is politeness. The model can read the skill file, disagree with it and proceed. A skill cannot refuse anything, so a rule you need obeyed does not become obeyed by being written in a skill. It becomes obeyed by a hook or a gate, and the skill is where the accompanying knowledge goes.

The second is that everything it loads lands in this window. A skill whose body dumps a reference manual on trigger has moved cost from the standing column to the invoked column and then blown the invoked column. That is what the bundled files are for, and it is the most common repair I make to a skill somebody else wrote.

The two tells that a skill is on the wrong rung

If its body is mostly reading and the model then works from what it read, it wanted to be a sub-agent, and section 3 explains why the window pays for that mistake. If the model keeps declining to follow it, it wanted to be a constraint, and no amount of stronger wording changes the rung it is on.

3 Sub-agent

A sub-agent is an object, and the summary it returns is the interface

A sub-agent runs the work in a window the parent never sees and hands back a report. It is the only rung that gives context back rather than taking it, and the price of that is a lossy, one-shot return. Design the return first. Everything else about a sub-agent is easier than that part and matters less.

rung 2 a context wall trigger the parent turn grant declared, not inherited
Standing cost
A name and one line, on every turn.
Invoked cost
A task prompt out. The reading, the searching and the false starts happen elsewhere.
What returns
A summary. Often a net saving, which no other rung can offer.
Boundary
Context. Its own window, its own grant, its own model if you want one.

3.1 · What it actually is

A markdown file in .claude/agents/. The frontmatter names it, describes when to use it, and declares the tools it may hold. The body is its system prompt, so the file is a job description rather than a procedure.

.claude/agents/charter-auditor.mda narrow grant and a stated return shape
---
name: charter-auditor
description: Audit a branch against the charter and report violations with
  file and line. Use when asked whether a change conforms, before opening a
  pull request, or when a reviewer disputes a rule.
tools: Read, Grep, Glob, Bash(git log:*), Bash(git diff:*)
model: sonnet
---

You audit changes against docs/charter.md. You do not fix anything and you
do not write files.

Return exactly this shape, and nothing else:
  one line per violation: path:line · rule id · what it violates
  then a final line: N violations across M files
If there are none, return exactly: no violations.

Two lines in that file do most of the work. The tools line is a grant, and a grant left off is a grant inherited by accident, which section 10 treats as a real defect rather than an untidiness. The return block is the interface. A sub-agent whose return shape is unstated returns prose, and prose is exactly what you were trying to keep out of the parent window.

3.2 · What the rung buys

  • Context back. Twenty file reads happen somewhere else and eight lines come home. This is the only rung with that property and it is the main reason to use it.
  • Parallelism. Several sub-agents at once, on independent work, and the wall-clock is the slowest one rather than the sum.
  • A narrower grant than the parent. A reviewer that cannot write. An auditor that cannot push. The grant is the security property, and it is declared in a file rather than hoped for in a prompt.
  • A different model or effort. Cheap and fast for mechanical fan-out, expensive and careful for the one judgment that matters.
  • Isolation when work collides. Parallel agents that mutate files need separate worktrees, and that is a flag rather than a design.

3.3 · The ceiling, which is the return value

The parent cannot see what the sub-agent saw. That is the point and it is also the whole cost. Three consequences follow, and the first two are avoidable.

  1. lossy Anything the report leaves out is gone. The parent re-derives it and you have paid for the same reading twice. Fix it by stating the return shape, including the fields you will need later, such as file and line.
  2. one shot The sub-agent cannot ask you what you meant halfway through. An ambiguous task becomes a confident wrong answer. Fix it by putting the disambiguation in the task prompt, not in the agent file.
  3. two models You now have two places to be wrong, and the parent has no way to check the child's work. This one does not have a fix. It has a price, and the four numbers in Keeping Capabilities Honest are how you find out what it is.
what crosses the wall, and what does notread the dashed edge as the thing you cannot recover
flowchart LR
  P["the parent turn"] -->|"task prompt, plus a grant"| A["the sub-agent"]
  A --> R1["forty file reads"]
  A --> R2["three wrong guesses"]
  A --> R3["the finding"]
  R3 -->|"the report, in the stated shape"| P
  R1 -.->|"never crosses"| P
  R2 -.->|"never crosses"| P
  class P dim
  class A pass
  class R1 block
  class R2 block
  class R3 warn

3.4 · When it is the wrong rung

A sub-agent for a job that needs three lines of knowledge is a context wall around nothing. You paid a task prompt, a fresh system prompt and a round trip to avoid loading a paragraph. The rule I use: if the work does not involve reading or searching more than the parent wants to hold, it is a skill.

The other direction is subtler. If the parent keeps asking follow-up questions about what the child found, the wall is in the wrong place. Either the return shape is too narrow, or that work belonged in the parent's own window all along.

4 MCP server

An MCP server is an interface with the implementation somewhere else

At this rung the capability stops being text the model reads and becomes a process the model calls. Another language, another machine, credentials it never sees. You buy determinism and a real trust boundary. You pay the largest standing bill on the ladder, and you pay it for every tool the server declares rather than for the ones you use.

rung 3 a process wall trigger the model, by schema standing every schema, every turn
Standing cost
Every declared tool's full schema, on every turn. Plus any instructions the server injects.
Invoked cost
Arguments out, and whatever the server chose to return.
What returns
A result, and no implementation. Its size is the server author's decision, not yours.
Boundary
Process. The model cannot read the code, so it cannot work around a bad schema.

4.1 · What it actually is

A server speaking the Model Context Protocol, connected over stdio to a local process, or over HTTP or server-sent events to a remote one. Where you declare it decides who gets it, and that is a layer decision in the sense Anatomy of a Charter uses: whoever owns the file owns the capability.

where a server is declared, and what that commits you toproject scope is a team decision
ScopeWho gets itUse it when
project · .mcp.jsoneveryone who checks out the repositorythe server is part of how this codebase is worked on
useryou, in every projecta personal tool, and you accept the standing cost everywhere
localyou, in this projecttrying it out, or holding credentials that must not be shared
.mcp.jsonchecked in, so it is a commitment to the team's context budget
{
  "mcpServers": {
    "incidents": {
      "type": "http",
      "url": "https://incidents.internal/mcp",
      // auth happens out of band. The model never holds the token
    },
    "repo-graph": {
      "command": "uvx",
      "args": ["repo-graph-mcp", "--root", "."]
    }
  }
}

4.2 · Three primitives, and only one of them is priced per turn

Most teams use MCP as a tool bus and never touch the other two. That is a budget mistake as much as a design one, because tools are the expensive primitive and resources are the cheap one.

what a server can offertools are the priced primitive
PrimitiveWho pulls itStanding costGood for
toolsthe model, when a schema matchesthe full schema of every declared tool, every turnactions, and lookups the model must decide to make
resourcesa human or the model, by referencenone per item. They are fetched, not advertiseddocuments, records, dashboards. Anything addressable
promptsa human, by namea name and a lineserver-side commands. Rung 0, shipped by whoever runs the server

A server's instructions are the fourth thing it can put in your window, and they arrive without being asked for. Read them before you enable the server in project scope. They are prose at rung 0 of the constraint chain, written by somebody who has never seen your charter, and the model cannot tell them apart from yours.

4.3 · What the rung buys

  • Determinism, though not exclusively. The same call returns the same answer. A bundled script promises that too, for nothing, which is why section 5 keeps repeatability off the list of reasons to build one.
  • Credentials the model never holds. The server authenticates. The model gets results. That is a genuine trust boundary rather than an instruction not to look.
  • Another machine. The capability can be the thing your platform team already runs, where they run it. Another language is not on the list, because a bundled script can be written in any language you like.
  • One implementation, many clients. A schema serves every agent and every editor you point at it. This is the reason to build a server rather than four skills.
  • A versionable contract. The schema is the interface. You can change what is behind it without touching a single charter.

4.4 · The bill, and the four ways to cut it

A server with twenty tools stands in your window twenty times, in full, on every turn, with every parameter description. The tools you never call are indistinguishable from the ones you do. Four cuts, in the order I would make them.

  1. first Turn off the servers this project does not need. Scope is the cheapest fix and the one nobody does, because enabling a server is a decision you make once and pay for daily.
  2. second Use deferred schemas where the client offers them, so a tool's full definition loads when it is searched for rather than standing in every turn. This turns rung 3's standing cost into something closer to a skill's.
  3. third Cut the tool count. Tools should be shaped like the task, not like the upstream API. Five task-shaped tools beat thirty endpoint-shaped ones on cost and on selection accuracy at the same time.
  4. fourth Cut the returns. A tool that answers a question should return the answer, not the payload it came in. Pages of JSON are an invoked cost the caller cannot refuse.

4.5 · The ceiling, and it is not the cost

The process wall runs both ways. You cannot see in, and the model cannot see in either. So a tool whose description is vague is a capability you cannot repair from the outside. On the skill rung a confused model can read the file and work it out. Here it can only call the thing and hope. The schema description is the capability as far as selection is concerned, which puts the craft of section 9 in someone else's repository when the server is not yours.

The second limit is a security property rather than a cost. What comes back through the wall is text, and it lands in the window next to your instructions. A server that returns a third party's content is returning an untrusted string into a context that treats strings as instructions.

the wall runs both ways, and the return is untrustedbrick is the part to treat as data, never as instruction
flowchart LR
  M["the model"] -->|"a call that matches the schema"| S["the server, another process"]
  S -->|"credentials the model never sees"| U["the upstream system"]
  U -->|"content written by other people"| S
  S -->|"a result, as text, into your window"| M
  X["your charter, in the same window"] --- M
  class M dim
  class S pass
  class U block
  class X warn

Prompt Injection covers that path properly. The capability-side rule is short: a tool that reaches content other people write needs its return treated as data, and the constraint that enforces it does not live in the server. It lives in your harness, where you can see it.

The server that should have been a script

One tool, your language, no credentials, no network, no other client. That is a script in a skill, and a skill costs one line where the server costs a schema on every turn. I have written this server twice and deleted it twice. The four questions in section 7 exist to stop me writing it a third time.

5 Server, CLI or API

A CLI is a tool server whose schema you are not paying for

Three things can reach an external system: a command-line tool the agent already has, a script that calls the API, or an MCP server. All three put a process between the model and the system. They differ in where the interface description lives, and that is what you are choosing between. Most teams reach for the third and needed the first.

cli schema in the model's head script schema in your file server schema in your window

Think about what a schema actually does. It tells the model what calls exist, what arguments they take, and what comes back. A CLI has all of that too. Its schema is the tool's own help text and the model's prior knowledge of it, and neither of those stands in your context window on every turn. That is the trade in one sentence, and the rest of this section is the cases where it goes the other way.

three ways to reach one external systemthe difference is where the interface lives
A CLI, run with BashA script calling the APIAn MCP server
interface livesin the tool's help, and in what the model already knowsin your file, which you wrotein your window, on every turn
standing costnonenonea full schema per declared tool
discoverythe model guesses flags. Sometimes wronglynone to guess. The script is the interfacetyped and advertised. The strongest of the three
auththe tool's own credential store. The model never sees a tokenan environment variable the script readsthe server's, including interactive sign-in flows
grant precisionexcellent. Bash(gh pr view:*) is one subcommandexcellent. One script, granted by nameper tool, and only for the tools it declares
output shapewhatever it prints. Yours to pipe and trimexactly what you returnwhatever the server author chose
other clientsanyone with a shellnobody. It is yoursevery client that speaks the protocol
fails bya wrong flag, retried. Turns spent guessinggoing stale when the API movesstanding cost, and a schema you cannot repair

5.1 · The order to try them in

  1. first · a CLI Is there a maintained command-line tool, already installed and already authenticated on this machine? Then use it, grant the exact subcommands, and write a skill that names the two or three invocations you actually want. The skill removes the guessing, which is the CLI's only real weakness.
  2. second · a script No CLI, or the CLI prints the wrong shape? A script in a skill directory calls the API and returns the field. You own the request, the retries and the output. No standing cost, and the determinism half of Keeping Capabilities Honest is mostly about this option.
  3. third · a server Reach for MCP when one of five things is true, and not otherwise: several clients or agents need the same access, sign-in is an interactive flow the model must not handle, the surface is broad enough that typed discovery is worth paying for, a vendor already ships a server you would otherwise reimplement, or you need a versioned contract other teams build against.
  4. then check Count the tools the server declares and multiply by turns per week. If you are enabling twenty schemas to call one of them, go back to the first row.
the same lookup, three waysthe first two cost nothing until they run
# a CLI, pinned in a skill so nothing is guessed
allowed-tools: Bash(gh pr view:*), Bash(gh pr diff:*)

2. Read the pull request with:
     gh pr view $NUMBER --json title,body,files
   Do not use other gh subcommands for this step.

# a script, when the CLI's output is the wrong shape
scripts/pr_summary.py $NUMBER   # prints five lines, not five pages

# a server, when four other agents need the same access
mcp__reviews__get_pull_request   # a schema, standing, every turn

5.2 · The honest case for the server

The CLI route has a cost that does not appear in the ledger. The model guesses a flag, the command fails, it reads the help text, it tries again. Three turns for a lookup. A typed schema makes that failure rare, and a broad surface makes it common, so the wider the tool you are reaching for, the better the server looks. A skill that pins the exact invocations closes most of the gap for a few commands and none of it for forty.

The second honest case is authentication. A CLI works because someone signed in on that machine. A server can own the sign-in flow, refresh the token, and keep the credential in a process the model cannot read. When the alternative is a token pasted into an environment variable in a script, the server is buying a real security property and not just tidiness.

The wrapper

Wrapping a CLI you already have in an MCP server is the most expensive way to gain nothing. You pay a schema on every turn for a capability Bash already provided, you lose the ability to pipe and trim the output, and you add a process to keep alive. If the reason is that the model keeps getting the flags wrong, the fix is a skill with the invocations written down.

Part III

Packaging and placement

Who owns a capability, and which rung it belongs on. Two sections. The first is the question that decides who may change the words, and the second is the question everyone asks first and should ask second.

6 Plugins

A plugin is not a rung. It is who owns the rungs

A plugin ships commands, skills, sub-agents, hooks and server declarations under one name, from one repository, with one owner. It changes nothing about how any of them work and everything about who decides what they say. That makes it the packaging question and the layer question at the same time, and it is a dependency in the ordinary sense: it changes under you.

packaging not a rung standing the sum of what it ships ships constraints too read those first

Before plugins there were two places a capability could live: your account, or this repository. Both answers are about you. A plugin adds a third, and the third one is about somebody else, which is why it is worth a section of its own rather than a line in the catalog.

the four places a capability can livethe last row is the only one that changes without you
Lives inWho owns the wordsWho gets itUpdatesRight when
user scopeyouyou, in every projectwhen you edit itpersonal habits, and nothing a teammate depends on
project scopewhoever reviews the repositoryeveryone with a checkoutthrough review, like codethis codebase's practices. The default, and it should stay the default
a plugin you publishyour teamevery project that enables itwhen you releasethree or more repositories need the same capability
a plugin you installsomebody elseevery project that enables itwhen they release. Read the diffa vendor or a community already solved it well

6.1 · What is in the box, and why all five parts matter

A plugin can carry more than capabilities, and the parts that are not capabilities are the ones to read first. Enabling a plugin can enforce rules, grant tool access, and put prose in your window on every turn, none of which is visible from its name.

what a plugin can shiptwo of these five are constraints, not capabilities
PartFamilyWhat enabling it means
commandscapability, rung 0new names a human can type. Cheap and visible
skillscapability, rung 1new standing lines, and new competitors for every trigger you own
sub-agentscapability, rung 2new standing lines, and grants written by somebody else
hooksconstraintcode that runs on your events. Read every one before enabling
server declarationscapability, rung 3, plus a trust boundaryschemas standing in every turn, and a process holding credentials

6.2 · The two costs a plugin adds that a local file does not

The first is arithmetic. A plugin's standing cost is the sum of everything it ships, not the part you wanted. Enable it for one skill and you are paying for its other six, plus any server it declares. That is the same bill as before and now somebody else decides its size.

The second has no equivalent locally. A plugin author has never seen your catalog, so their descriptions cannot avoid colliding with yours. The sibling test from section 9 now has to run across a boundary you do not control, and the loser of a collision fails silently, which means an update can quietly disable a skill of your own that used to fire.

  1. on install Read the descriptions, not the README. Descriptions decide triggers, and they are the part that competes with your own capabilities.
  2. on install Read every hook and every server declaration. These two can enforce and can hold credentials, and neither is what most people think they are enabling.
  3. on install Count the standing lines it adds and say the number out loud. If the plugin does not document it, count it yourself with /context before and after.
  4. on update Diff the descriptions. A body change is a behavior change you will notice. A description change is a trigger change you will not.
  5. quarterly Ask which of its capabilities actually fired. A plugin is the easiest place for dead weight to hide, because nobody feels responsible for pruning somebody else's package.

6.3 · When to publish one

Three repositories is my threshold. Below that, project scope is simpler, reviewable in the normal way, and impossible to forget you have. Above it, copying the same skill by hand starts producing four versions that drift, which is the failure a package exists to prevent.

The other good reason is handing someone a working setup rather than instructions. A team that owns a domain, its practices, its checkers and its access can ship all of that as one thing. That is the strongest form of the layer idea in Anatomy of a Charter: the people who own the practice own the file that carries it, and consumers get the next version without being told.

What makes a good plugin

One theme, so a consumer can say what enabling it means. A documented standing cost, in lines and in schemas. Names specific enough not to collide with a local catalog it has never seen. Hooks and server declarations kept separable from the skills, so someone can take the knowledge without the enforcement. And a changelog that treats a description edit as a breaking change, because for triggers it is one.

7 Choosing a rung

Four questions, in order, and the default answer is a skill

Placement on this ladder is not a judgment about how important the capability is. It is four facts about the work: who notices the need, whether the work should stay out of this window, whether it needs a process wall, and how often it fires. Answer them in order and stop at the first one that decides.

1 who notices 2 which window pays 3 process wall 4 how often

7.1 · The procedure

  1. before all four Run the family test. If removing the thing lets the agent do more, you are placing a constraint and this is the wrong ladder. Wanting it obeyed is not a reason to write it as a capability, and writing it as a capability is how it stops being obeyed.
  2. question 1 Who notices the need? A human, at a moment of their choosing, means a command. Turn model invocation off and the standing cost goes to zero. If the answer is that it must happen every time, go back to the line above.
  3. question 2 Does the work belong in another window? Bulk reading, wide search, exploration with dead ends, anything whose byproduct is bigger than its answer, means a sub-agent. This is the only rung that hands context back, so spend it where the byproduct is the problem.
  4. question 3 Does it need a process wall? One of the five reasons in section 5 means an MCP server: several clients need the same access, sign-in is an interactive flow, the surface is broad enough that typed discovery pays for itself, a vendor already ships it, or other teams need a versioned contract. Repeatability is not on that list, because a script already gives you that.
  5. question 4 Otherwise it is a skill. Then ask how often it fires. Weekly or more earns its standing line. Monthly is a skill whose description you should sharpen or a bundled file on a skill you already have.
the decision, as askedstop at the first question that decides
flowchart TD
  Q0{"remove it: can the agent do more"} -->|"yes"| CON["a constraint. Wrong ladder"]
  Q0 -->|"no"| Q1{"who notices the need"}
  Q1 -->|"a human, when they choose"| CMD["command"]
  Q1 -->|"the model, mid-task"| Q2{"is the byproduct bigger than the answer"}
  Q2 -->|"yes"| AG["sub-agent"]
  Q2 -->|"no"| Q3{"credentials, several clients, a broad surface, a vendor server, or a versioned contract"}
  Q3 -->|"yes"| MCP["MCP server"]
  Q3 -->|"no"| SK["skill"]
  class Q0 dim
  class Q1 dim
  class Q2 dim
  class Q3 dim
  class CON block
  class CMD warn
  class SK warn
  class AG pass
  class MCP pass

7.2 · One job, four rungs

Take the job from section 1: check a branch against the charter. All four rungs can carry it. They carry it at four prices, and they fail four different ways.

the same capability at every rungthe failure column is the one to choose on
As aWhat it costsWhat it buysHow it failsRight when
command nothing standing. The charter and the diff on invocation a rehearsed prompt, run when a person decides nobody types it before the pull request the check is advisory and the author is the reader
skill one line standing. The procedure plus the charter on trigger the model checks without being asked the charter is long, and now it is in your window on every conforming change too the charter is short and the check is part of doing the work
sub-agent one line standing. A task out, a violation list back the charter and the whole diff read elsewhere. Eight lines come home the report omits the line numbers you needed the charter is long or the diff is wide. My default for this job
MCP server a schema standing, on every turn, forever the same verdict every time, and every client gets it you have built a linter with extra steps the rules are decidable. Then it is a gate, and the ladder changes

The last row is the useful one. When a capability at rung 3 turns out to be fully decidable, it has stopped being a capability. It is a check, and checks belong on the constraint chain where they can refuse things. Noticing that transition early saves you an MCP server and gets you a gate that actually blocks.

Where placement stops being a mechanics question

These four questions settle the first placement. They do not tell you when to move a capability that is already running, and they do not price the standing line it holds. Keeping Capabilities Honest carries both: the two prices in full, the signals that promote or demote a capability, and the numbers that tell you whether the one you installed ever fires.

Part IV

Quality

A capability placed correctly still fails if nobody finds it, if it may touch the wrong things, or if what comes back is not usable. Five sections, and four of them are about one line of frontmatter and one paragraph about the return.

8 A good one

What a good one looks like, one rung at a time

Placement is the first decision and it is not the one that decides whether the capability works. These are the checklists I run before committing a capability, and the tell at the end of each is the symptom I look for when somebody says theirs does not work. Four rungs, four lists, and one property runs through all of them: say what comes back.

one job per capability one trigger stated in the caller's words one return shaped on purpose

8.1 · A good command

  1. Does one job, and its name is a verb doing it. charter-check, not helpers.§1
  2. Declares argument-hint, so the person typing it knows what it wants without opening the file.§1
  3. Brings its own evidence. The ! injection collects the diff, the log or the failing test so the prompt arrives complete.§1
  4. Says what not to do. Report, do not fix is the difference between a review and a surprise commit.§1
  5. Narrows allowed-tools to the exact commands it injects, because it is running a shell for you.§10
  6. Turns off model invocation unless you want the description standing in every turn.§1
  7. Short. A page of policy in a command file is a skill wearing the wrong frontmatter.§2

The tell of a bad one: it opens with a request for help and ends with the model asking which files you meant. A command that has to ask questions was a note, not a capability.

8.2 · A good skill

  1. Its description says what, when, and when not, in the words a caller would use rather than the words your code uses.§9
  2. Names the sibling it is not. The negative trigger is what resolves a collision, and the loser of a collision fails silently.§9
  3. The body is a numbered procedure, not an essay. Steps in order, with the refusals stated where they apply.§2
  4. Reference material lives in bundled files. A table in the body is paid for on every trigger, whether the job needed it or not.§2
  5. Bundles a script for anything decidable, and says run it, do not reimplement it.§2
  6. Does one job. If the name needs an and, it is two skills and the description will match neither cleanly.§9
  7. Scopes allowed-tools to its own scripts, so the grant is the narrow one rather than the session's.§10
  8. Ships six test requests: three that should fire it, three near misses that should not.§9

The tell of a bad one: you cannot say in one sentence when it should fire. If you cannot, the model is inferring it from the same words you just failed to summarize.

8.3 · A good sub-agent

  1. States its return shape literally, including the fields the parent will need later. File and line, not a summary of findings.§3
  2. Declares tools. An omitted grant is the widest one, and the widest one is never what the file meant.§10
  3. Holds the narrowest grant that does the job. An auditor that cannot write is a different component from one that can.§10
  4. Says what it must not do: not fix, not push, not ask. It cannot ask, so the prompt has to have decided.§3
  5. Produces one deliverable. A sub-agent is not a conversation, and anything it discovers outside the deliverable is lost.§3
  6. Chooses its model and effort deliberately. Cheap and fast for mechanical fan-out, careful for the judgment that matters.§3
  7. Runs the checker rather than forming an opinion, wherever the question is decidable.§2
  8. Gets a worktree when several of them mutate files at once, and does not when they do not.§3

The tell of a bad one: the parent asks a follow-up question every single time. The wall is in the wrong place, or the report shape was never designed.

8.4 · A good server, and a good reason to have one

  1. Declares few tools, shaped like the task rather than like the upstream endpoints.§4
  2. Returns the answer, not the payload the answer arrived in. The caller cannot refuse what you send.§4
  3. Writes each schema description as an interface, because from the outside it is the only part of the capability anyone can read.§9
  4. Is declared in the narrowest scope that needs it. Project scope charges the whole team on every turn.§4
  5. Keeps read tools separable from write tools, so a permission rule can grant one without the other.§10
  6. Exists for one of five reasons: several clients, an interactive sign-in, a broad surface worth typing, a vendor implementation, or a contract other teams build against.§5

The tell of a bad one: it wraps a CLI you already had. You are paying a schema on every turn for something Bash did for free, and you have lost the ability to trim the output.

The property all four share

Every list above has a line about what comes back. A command says what not to do, a skill says which file to read, a sub-agent states its report shape, a server returns a field rather than a payload. Capability work is interface work, and the return half of the interface is the half that gets left to chance.

9 Discovery

The description is an interface, and you are writing it for a reader who has one line

Three of the four rungs are selected by a model reading a description. That description has two jobs and they pull against each other. It has to be short, because it stands in every turn. It has to be specific, because a vague one never matches. Almost every dead capability I have looked at died in that sentence.

short it is a standing cost specific it is the trigger distinct siblings compete

Write the description in the words the work arrives in, not the words the implementation uses. A skill named after your internal abstraction, described in terms of your internal abstraction, matches a request nobody makes.

the same capability, described twicethe first one never fires
# never matches
description: Helpers for working with the persistence layer.

# It says what the author built. A user says "add a column",
# "the migration failed", "can we drop this index". None of those
# words appear, so nothing matches.

# matches
description: Write, review or roll back a database migration in this
  repository. Use when the user mentions a migration, a schema change,
  adding or dropping a column, an index, or a backfill. Not for query
  performance work, which belongs to the query-tuning skill.

9.1 · Three parts, and the third one is the one people leave off

  1. what it does One clause, active voice, the verbs the job actually involves. Write, review or roll back rather than handles.
  2. when to use it The trigger surface. Concrete nouns and verbs a person would type. This is the part that does the matching, and it is worth more words than the first part.
  3. when not to The negative trigger, naming the sibling that owns the neighboring job. This is what resolves a collision, and it is the part almost nobody writes.

9.2 · The sibling test

Take every pair of capabilities in scope and say the sentence that tells them apart. If you cannot say it in one sentence, the model cannot infer it from two descriptions. You then have two choices and both are fine: merge them, or add the negative trigger to each. Leaving the overlap is the option that is not fine, because the loser of a collision fails silently and you will conclude that it does not work.

collisions, and the shape of the repairthe general capability wins by default
The collisionWhat happensRepair
a general skill and a specific onethe general one wins, and the specific rules never applynarrow the general description, and name the specific one in it
two skills with the same nounsselection becomes a coin flip you cannot seemerge, or split along the verbs instead of the nouns
a skill and a sub-agent for one jobeither could fire, and they cost very different amountskeep the sub-agent for the reading, and let the skill call it
an MCP tool and a bundled scriptthe tool wins, because a schema is more visible than a step in a filepick one. Two implementations of one job is a maintenance bug as well

9.3 · Test the description, because it is the part that runs most often

A description is testable and almost nobody tests it. Write down three requests that should fire the capability and three near misses that should not. Start a fresh session for each and see what happens. Six runs tell you more about whether a skill works than reading its body ever will.

This is the smallest useful eval on the capability side, and it has the property that The Eval Loop asks for: a golden set, a distribution rather than one green run, and a number that moves when you edit the description. Keep the six cases in the skill directory. They are the only regression test a trigger can have.

Naming, briefly

Name a capability for the job, in the caller's language, with a verb in it where a verb fits. migration-writer, charter-auditor, incident-lookup. Never utils, helper, manager or the name of your internal module. The name is read as part of the trigger, so a name that says nothing wastes the cheapest tokens you have.

10 Grants

The grant is where the two ladders touch, and an omitted grant is the widest one

Every capability runs holding some set of tools. That set is the one attribute that is a capability property and a constraint rung at the same time: on the constraint chain a tool grant sits just above prose, because it makes a whole class of action unrepresentable rather than discouraged. Leave the line out and you have written the widest grant available, silently.

declare it in the file omitted means inherited narrow beats instructed

A sub-agent told not to write files is a sub-agent that may write files. A sub-agent granted Read, Grep, Glob cannot write one. The difference is the difference between prose and a mechanism, and it costs one line of frontmatter, which is why Constraint Engineering puts the tool grant on the constraint chain rather than in the settings.

where each rung declares its grantall four have a place for it
RungDeclared asIf you omit itWorth narrowing when
commandallowed-tools in frontmatterthe session's own permissions applythe command injects shell output. Then narrow to the exact commands
skillallowed-tools in frontmatterthe session's own permissions applythe skill bundles scripts. Grant those scripts and nothing else
sub-agenttools in frontmatterit inherits, which is almost never what you meantalways. A reviewer that cannot write is a different component
MCP serverpermission rules per tool, and the scope you declared it inevery declared tool is callablethe server has write or destructive tools beside its read ones

10.1 · Composition, and the one thing that does not cross

Capabilities call capabilities. A command can name a skill. A skill can tell the model to dispatch a sub-agent. A sub-agent can hold MCP tools and load skills of its own. What does not cross is knowledge of what happened: the parent gets the report and nothing else, which is the property you bought and also the one that bites.

a capability graph, with the grants markedbrick edges are the ones to declare rather than inherit
flowchart TD
  H["a human types a command"] --> C["command · a rehearsed prompt"]
  C --> M["the parent turn"]
  M -->|"description matched"| S["skill · procedure plus references"]
  S --> SC["a bundled script · deterministic, no standing cost"]
  M -->|"dispatch, with a declared grant"| A["sub-agent · its own window"]
  A --> T["MCP tools, if granted"]
  A -->|"a report in the stated shape"| M
  T -->|"untrusted content"| A
  class H dim
  class C warn
  class M dim
  class S warn
  class SC pass
  class A pass
  class T block

Two rules keep that graph from turning into a maze. First, a capability may compose downward and never sideways: a skill dispatching a sub-agent is fine, and two skills that call each other are one skill with a naming problem. Second, the grant travels with the callee, not with the call. If a sub-agent needs a write tool for one task and not for another, that is two sub-agents, and the second one is the safe default.

The grant leak

A sub-agent file with no tools line is the most common real defect on this page. It reads as tidy and it means everything the parent had. The auditor can write. The researcher can push. Nothing goes wrong until the day something does, and the transcript then shows a component doing exactly what you never granted it and never forbade.

11 The catalog

Every capability primitive, and the neighbors that get mistaken for one

Eleven primitives carry capability. Three more package them. Six things sit next to them and belong to other families, and knowing which is which is most of what keeps a charter readable. The standing column is the one to read down.

the catalog, by rung then familyread the standing column down
PrimitiveRungInvoked byLives inStanding costBoundary
the four rungs
command0a human, by name.claude/commands/a line, or nonenone
namespaced command0a human, by path.claude/commands/<group>/a line, or nonenone
MCP prompt0a human, by namethe servera linenone. Someone else's rung 0
skill · project1the model, or a human.claude/skills/a lineconvention
skill · personal1the model, or a human~/.claude/skills/a line, in every projectconvention
bundled reference1the model, when the body says toa skill directorynonenone. It is a file
bundled script1the model, by running ita skill directorynoneprocess, for free
sub-agent2the parent turn.claude/agents/a linecontext
MCP tool3the model, by schemathe servera full schema, every turnprocess
MCP resource3a human or the model, by referencethe servernone per itemprocess
server instructions3nobody. It arrivesthe serverwhatever the author wrotenone, and that is the problem
packaging · not capability, but it decides who has one
pluginenabled per projecta marketplace or a repositorythe sum of what it ships
project scopeeveryone with the checkout.claude/, .mcp.jsoncharged to the whole team
user scopeyou, everywhere~/.claude/charged in every project
adjacent · reaches for the same file and belongs to another family
hookan eventsettingsnone in the windowconstraint. See the other page
permission rulea tool callsettingsnone in the windowconstraint, and the cheapest one
memory · CLAUDE.mdnobody. It is always therethe repository, or the accountthe whole file, every turnstate. It needs an expiry
output stylea session settingsettingssmallsetting
model and efforta session or a calleesettings, or frontmatternonesetting
scheduled and background worka clock, or a parent jobthe factorynone in this windowfactory. See The Factory

Two rows in the adjacent group deserve their reputation for causing trouble. Server instructions are prose you did not write, standing in your window, indistinguishable from your charter. A memory file is state with no layer, no scope and no expiry, and it governs work it has never seen. Both are the cheapest ways to end up with a rule nobody agreed to.

12 Anti-patterns

Twelve anti-patterns, and every one is a symptom of a missing attribute

None of these are failures of effort. Each one is what you get when a capability was installed with three of its four attributes designed. Read the symptom column as a diagnosis: it names which of trigger, rung, grant or return was left to chance.

twelve anti-patternsthe symptom column is the diagnosis
NameWhat it looks likeSymptom ofRepair
the unread skillinstalled, described in the author's vocabulary, never fires on its ownno trigger designrewrite the description in the caller's words. Then test it six times
the kitchen sinkone skill for six unrelated jobs, so it matches everything and helps with nothingno trigger designsplit along the verbs. One job, one description
the twintwo capabilities whose descriptions overlap. The general one always winsno trigger designnegative triggers in both, or merge them
the rule in a skill's clothinga skill that only says always do X, and gets ignored on the turns that matterwrong ladderput the rule on the constraint chain. Keep the knowledge in the skill
the context bomba skill body that loads a reference manual on triggerwrong rung for the weightprocedure in the body, tables in bundled files
the standing taxa server enabled in every project for a tool used monthlyno cost accountingnarrow the scope, or defer the schemas
the API mirrorthirty tools shaped like upstream endpoints, so the model must assemble the task itselfno return designfive task-shaped tools. Let the server do the assembling
the chatty returna tool that answers a question with the payload the answer arrived inno return designreturn the field. The caller cannot refuse what you send
the blind parenta sub-agent report that omits what the parent needed, so the parent reads it all againno return designstate the return shape in the agent file, fields included
the grant leaka sub-agent file with no tools line, holding everything the parent heldno grant designdeclare the grant. A reviewer that cannot write is a different component
the server that should be a scriptone stdio server, one tool, your language, no credentials, no other clientrung inflationa bundled script. No standing cost, same determinism
the undeletable capabilitynobody knows whether it fires, so nobody dares remove it. The catalog only growsno evidencecount invocations, then hold a deletion day

Count the symptom column and the distribution is the point. Four are trigger failures, three are return failures, and only two are about the mechanism at all. Capability work is mostly interface work, done in one line of frontmatter and one paragraph about what comes back.

Where this page stops

Everything above is what a capability is and how to write a good one. None of it tells you what the set of them costs you next quarter, whether the ones you wrote still fire, or how to get a repeatable answer without buying a rung to hold it. Keeping Capabilities Honest is that half: two prices, five kinds of rot, determinism bought at the cheap end, and the four numbers that make a silent failure audible.