The Taxonomy
One shared vocabulary for what agents can do: how ids are structured, matched, aliased, and retired.
Status: Draft. Not ratified yet. Questions go to the taxonomy owner (see the review process).
The rule in one line
Every capability id comes from one shared, governed, hierarchical taxonomy. The Registry rejects any id that is not in it. Free-form ids are not allowed.
The problem
Without a shared vocabulary, three providers describe the same skill three ways:
- Provider A registers
crop-disease-detect - Provider B registers
plant_pathology - Provider C registers
leafspot-ai
A seeker searches for “crop disease” and finds only Provider A. Two real matches are lost. This is not a made-up risk. It is how A2A works today: each agent invents its own skill names, and registries keyword-search them. UAI sits above A2A exactly to fix this. So all three providers must register against one id:
urn:capability:agriculture:crop_health:disease_diagnosis
Now the same search finds all three.
The id format
urn:capability:agriculture:crop_health:disease_diagnosis
| | |
domain area task
Rules: lowercase only, underscores inside a segment, colons between segments. Stored in Postgres as the ltree path agriculture.crop_health.disease_diagnosis.
One taxonomy node, all fields
{
"id": "urn:capability:agriculture:crop_health:disease_diagnosis",
"parent": "urn:capability:agriculture:crop_health",
"status": "active",
"description": "Takes crop symptoms, images, or sensor data. Returns the likely disease with a confidence level.",
"examples": [
"identify leaf spot on tomato from a photo",
"flag wheat rust from field sensor readings"
],
"aliases": ["disease_detection", "plant_pathology"]
}
Every node needs a real description (input and output, one sentence each) and at least two examples. The examples are not decoration. They are what a reviewer, and later the Seeker-side AI, use to tell two similar nodes apart.
Matching, with match_type
A provider registers at the task level. A seeker may ask at the area level. Subtree matching connects them, and each result says how it matched:
{
"query": "urn:capability:agriculture:crop_health",
"matches": [
{
"did": "did:webvh:...:example.org:agent-a",
"capability": "urn:capability:agriculture:crop_health",
"match_type": "exact"
},
{
"did": "did:webvh:...:example.org:agent-b",
"capability": "urn:capability:agriculture:crop_health:disease_diagnosis",
"match_type": "descendant"
}
]
}
match_type lets a seeker prefer exact matches without the Registry doing any ranking. The idea comes from the service-matching literature (exact and plugin degrees of match).
Aliases: one meaning, many words
disease_detection and plant_pathology are aliases. They resolve to the canonical id. They do not create new nodes.
Rule of thumb: if a seeker searching term A would want term B’s results, make it an alias. If providers genuinely do different work, make a new node.
- “leaf_spot_detection” vs disease_diagnosis: same work, alias.
- “soil nutrient analysis” vs disease_diagnosis: different work, new node.
The life of an id
pending -> active -> deprecated -> retired
- pending: usable right away while under review. Discovery responses mark it as pending.
- active: fully accepted.
- deprecated: carries a replacement pointer and a sunset date. New registrations against it are rejected after the sunset date. Default grace window: 90 days.
- retired: no longer registrable against. The id is never deleted and never reused.
Example deprecation:
{
"id": "urn:capability:agriculture:crop_health:pest_id",
"status": "deprecated",
"replaced_by": "urn:capability:agriculture:crop_health:pest_diagnosis",
"sunset": "2026-10-31"
}
Why never delete: an audit receipt from last year may reference pest_id. The id must stay resolvable forever, even if no one can register against it anymore.