Reusable Agent Capabilities Are a Routing Problem, Not an Authoring Problem
Every system that runs an agent for long enough accumulates procedural knowledge. How this organization classifies an escalation. What a valid reconciliation looks like before it may be written back. The five checks that precede an irreversible action. Which fields to pull, in what order, when a document arrives from a particular counterparty. This knowledge is real, it is specific to you, and it does not live in the model. It has to be encoded somewhere in your system, and the question of where turns out to be one of the more consequential design decisions in an agentic architecture.
The obvious answer is to write the procedure down and hand it to the agent. That is where reusable capabilities come from: a named, self-contained package of procedural instructions the agent can pull in when it is doing the kind of work the package describes. The packaging is trivial. Anyone can write the procedure. What is not trivial, and what almost entirely determines whether the capability earns its place, is the mechanism that decides when it enters the run at all. A capability that is never selected is indistinguishable from a capability that was never written. A capability that is selected on the wrong request is worse than one that does not exist, because it steers a run that would otherwise have gone fine. The authoring is the easy half. The routing is the engineering.
Procedural knowledge has to live somewhere, and the always-on layer is the expensive place
Start with the alternative, because it explains what capabilities are actually for. The simplest place to put procedural knowledge is the standing instruction layer: the configuration that loads on every run, unconditionally, before the agent sees the request. It works, it is deterministic, and for a small body of universal rules it is exactly right. Anything that must shape every single run, regardless of what the run is about, belongs there and nowhere else.
The problem is that this layer is unconditional, and unconditional means universal. A procedure written for one situation in twenty is still read on the other nineteen, out of the same finite budget the actual work is drawing on. A team that keeps appending procedures to the always-on layer is building a system where a request to summarize a status update carries the full protocol for handling a disputed chargeback. The billing is the visible half of that, and it is the less interesting half. The real damage is that an instruction set covering twenty situations hands the model nineteen things to rule out before it can act on the one that applies, and it has to do that ruling out on every request, forever. The layer that is always present is the layer you should be most reluctant to grow.
So the real function of a reusable capability is not that it saves you from re-describing a workflow. It is conditional loading. A capability lets a substantial body of procedure sit outside the run at zero cost until the moment it is relevant, and enter the run in full only then. That is a context-economics move before it is an ergonomics move, and reading it that way changes what you optimize. You are not building a snippet library. You are building a system that decides, per run, which procedures are worth paying for.
The description is the interface, and the body is inert
The mechanism that makes conditional loading possible is a two-tier structure, and everything downstream follows from it. The capability’s identity, its name and a short description of what it does and when it applies, is always resident. It is cheap, and it is present on every run. The body, the actual procedure, is not loaded until the capability is selected. At rest, a library of fifty capabilities costs you fifty short descriptions. Only the ones that fire cost anything more.
This puts an unusual load on the description. It is the whole of what the selection decision has to go on, and the body stays invisible unless that decision goes well. When the agent weighs whether to pull in the escalation procedure, it is not reading the escalation procedure. It is reading one or two sentences about the escalation procedure and inferring, from what has been asked of it, whether those sentences apply. The body could be immaculate, exhaustive, and exactly correct, and it will never run if the description does not connect it to the situations it was built for. The quality of a capability, as experienced by the system, is bounded above by the quality of its description.
There is a second consequence that is easy to miss. The selection surface for capabilities is the same kind of surface as the selection surface for tools, and they are read together. Both are short natural-language descriptions competing for the same decision. A capability whose description overlaps with a tool’s description does not sit politely in a separate namespace; it muddies the choice between them. Anyone who has watched an agent route to a plausible-but-wrong tool because two descriptions were near neighbors already knows what this failure feels like. Adding a capability library adds a second population to that same crowded space.
Activation is the axis you design along
Reuse is the label these get filed under, and it is the least load-bearing thing about them. What actually determines where a piece of procedural knowledge belongs is its entry condition: what admits it into a run, and how much slack that trigger tolerates.
There are four of them, and they are not interchangeable. Standing configuration activates unconditionally, on every run, decided by no one. A model-invoked capability activates when the agent judges, from its description, that it is relevant; the model decides, probabilistically, at runtime. A caller-invoked capability activates when something outside the model asks for it by name, whether that is an operator choosing it explicitly or an upstream service naming it in a request; the decision is deterministic and it is made outside the agent. A deterministic interception point activates on a system event, before or after a specified action, with no model judgment involved at all.
Sort by how much slack the trigger can tolerate. No slack and universal reach: standing configuration, and you pay for it on every run. No slack and a specific event: an interception point, where no capability of any kind will do. Slack you can live with, because a miss costs you quality rather than a guarantee: a model-invoked capability. No slack inside the model but full knowledge outside it: caller-invoked, and the price is that something upstream has to know the capability exists.
One thing a capability is not is a worker. A delegated agent is a second process; it gets a context you can bound and a budget you can cap. A capability is procedure, not process. It executes in the caller’s context, on the caller’s budget. It may narrow the set of tools it is permitted to touch, but that is a permission boundary, not an isolation boundary, and the two are easy to confuse. Invoking a capability buys you no context isolation whatsoever. Everything the procedure does, every intermediate result it produces, and every token it consumes lands in the same context window that was already doing the work. Reaching for a capability when what you needed was a bounded, disposable context is a common and quiet mistake, and it shows up as a run that bloats for no visible reason right after the capability fires.
A must-happen rule cannot sit behind a probabilistic trigger
The most damaging thing you can do with a model-invoked capability is put a requirement inside it.
Consider a rule that any customer communication touching an account closure has to carry the required retention disclosure. Encoded as a model-invoked capability, that rule is now conditional on the agent recognizing, from a short description, that the message in front of it is the kind of message the rule was written for. Most of the time it will. The times it does not, nothing announces itself. The message goes out, well written and helpful, missing the one paragraph that was not optional. The run completes, the logs show a clean execution, and the control simply was not applied. No exception is raised, because nothing was broken in any sense the runtime understands. The capability was available and was not chosen, which is the system working exactly as designed.
This is the line that matters: capabilities encode expertise, not enforcement. Expertise is knowledge the agent applies well when it recognizes the need, and where a miss degrades quality rather than violating a guarantee. Enforcement is a property the system must hold regardless of what the model concludes, and it belongs in code, on a deterministic path, where it executes whether or not anyone thought about it. If a control has a compliance owner, an audit requirement, or an irreversible consequence, it does not go in a capability. It goes in the part of the system that does not have opinions.
The tell is easy to check. Ask what happens on the run where the capability does not fire. If the honest answer is “the output is a bit worse,” a capability is fine. If the honest answer is “we have a violation and we would not know,” you are one probabilistic judgment away from an incident, and you have put a guarantee somewhere that cannot make one.
Both ways a description can fail are silent
A description can fail in exactly two directions, and neither of them produces a signal.
A description that reaches too far will be selected by work the procedure was never designed to handle. It is tempting to treat this as harmless, since the procedure is at worst irrelevant, but a false activation is not free. It loads a body of procedure into the context, it consumes budget, and, worst of all, it steers. An agent handed a detailed procedure tends to follow it, and a detailed procedure for the wrong situation is a confident push in the wrong direction. The output is not obviously broken. It is just shaped by a workflow that had no business being there, which is considerably harder to notice than an error.
A description drawn too tightly is simply never matched. The work it was built for arrives phrased a little differently than the description anticipated, no match is made, and the agent handles the situation from general knowledge instead. The result is usually acceptable, which is precisely the problem. An unselected capability leaves no trace at all: it does not fail, it merely never happens. What you get is a system that quietly does not know something you taught it, and an output that looks like ordinary work. Nothing distinguishes a capability that is doing its job from one that has never once been chosen, which is how a capability can sit in a library indefinitely with no one aware that it is inert.
Both failures share a root: activation is a probabilistic judgment made against a compressed representation of the capability, and a bad judgment produces no exception. This is the operational fact that should govern how you treat the whole category. Nothing in the system will tell you that your routing is wrong.
A capability library does not scale linearly
The instinct, once the pattern works, is to accumulate. Every recurring procedure becomes a capability, the library grows, and the system looks steadily richer. The arithmetic that defeats this is already familiar from tool catalogs, where the tenth entry does not simply add itself but makes the nine before it harder to reach. Capabilities inherit that arithmetic.
They inherit it with a twist that makes it worse, because they are not accumulating in a namespace of their own. They are joining a selection surface the tools already occupy. Two capabilities that both concern refunds, separated only by a condition one of them states in a subordinate clause, are a genuinely ambiguous routing decision. But a capability whose description shades toward a tool’s does not merely make two capabilities harder to tell apart. It makes a tool harder to reach. The sizing question is therefore not how many capabilities the library can hold. It is how many descriptions, of any kind, this agent can still sort, and that budget is shared with a population that was already there.
The answer is smaller than you would like. What you want is a set of capabilities close to orthogonal, each covering ground the others clearly do not, distinguishable by a reader without hesitation. If you cannot state crisply what separates two of them, the model cannot either. Fold one into the other, or drop it. That is not housekeeping. It is how the routing decision stays solvable, and it is never finished: a capability that has drifted out of relevance should be pulled with the same energy it was added, because a stale capability that still matches is a liability the system never chose to take on. It fires on its own, it encodes a procedure the organization has outgrown, and nobody decided to apply it.
A shared capability is an interface with consumers you do not control
Capabilities acquire scope the moment more than one person or service uses them. Some are personal, used by one operator in their own runs. Some are shared across a team or a project, and travel with the system as part of its definition. Some are mandated centrally, deployed across an organization and not overridable locally, because they encode something the organization has decided is not negotiable. These are ordinary configuration-scope concerns, and they behave the way scoped configuration always behaves: the narrower scope is more specific and more contextual, the broader scope is more authoritative, and conflicts between them need a resolution rule you write down, not one you infer from a production surprise.
What is specific to capabilities, and genuinely easy to get wrong, is that the description is part of the shared contract. Changing a capability’s body changes what it does when it runs, which is the kind of change people review carefully. Changing a capability’s description changes when it runs, across every consumer, which is the kind of change people make casually because it looks like editing a comment. Broadening a description to catch a case one team was missing can start pulling the capability into a neighboring team’s runs, where it now steers work it was never designed for. Nothing about that change looks dangerous in a diff.
Treat the description with the seriousness you would give any published interface. It has consumers, they depend on its current matching behavior, and the fact that its semantics are expressed in prose rather than in types does not make them any less load-bearing.
Activation is invisible unless you make it visible
Because both routing failures are silent, a capability library will never tell you how it is doing. You have to instrument the routing decision itself. Record which capabilities were selected on which runs, and by extension which were available and not selected. Without that record you cannot answer the two questions that matter. Is this capability ever firing? When it fires, is it firing on the work it was written for?
That record is what turns capability design from an act of faith into something you can iterate on. A capability that never appears in the log is a description problem, not a content problem, and no amount of improving the procedure will fix it. A capability that appears on runs it has no business touching is over-broad, and the fix is to sharpen the description, not to add caveats to the body. A capability that fires correctly and whose runs are no better than the runs where it does not fire is a capability that should not exist. All three of these are diagnosable, and none of them are diagnosable without the log.
The same instrumentation is what makes the library curatable at all. Deciding to merge two overlapping capabilities, or to retire one that has drifted out of relevance, is a judgment call in the abstract and an obvious call when you can see how each one has actually been selected over time.
Design the trigger, then write the procedure
The discipline that falls out of all of this inverts the order most people work in. The instinct is to write the procedure, since that is the part that contains the knowledge, and then to summarize it into a description as an afterthought. That is backwards. The description is the part the system reads, the part that determines whether the procedure runs at all, and the part where both silent failures originate. It should be written first, and it should be written as a routing specification: this is the situation this applies to, and here is what distinguishes that situation from the adjacent ones. If you cannot draw that boundary in a sentence or two, the capability is not well enough defined to build yet, and no amount of care in the body will rescue it.
Everything else follows from taking activation seriously. Put the universal rules in the always-on layer and keep that layer small, because you pay for it on every run. Put the situational expertise in capabilities and accept that entry is probabilistic, which is tolerable precisely because a miss degrades quality rather than breaking a guarantee. Put the guarantees in code, on a deterministic path, where no judgment is required and none is permitted. Put the work that needs its own context and its own budget in a delegated agent, and stop expecting a procedure to give you isolation it structurally cannot provide. Keep the library small enough that its members stay distinguishable, log what fires, and retire what has stopped being relevant.
A reusable capability is among the most valuable things you can add to an agentic system, because it is how the system comes to know how your organization actually does its work. But that value is delivered through a judgment you do not control and cannot see fail. That is the thing to design around. The procedure is the part everyone writes. The trigger is the part that determines whether any of it was worth writing.
