HIP-0140: Proposing a Capability
Abstract
HIP-0139 says what a capability is. This says how one comes to exist, and how one that exists grows.
A capability is proposed as a HIP, numbered at 1000 or above, before any of it
is served. The HIP names the thing, the address, the boundary it owns and what a
customer is promised. It enters at alpha behind a flag, reaches beta when a
customer can be let in, and is promoted to ga when its surface is published to
every generated client, tool, command and page. Every step is proved by a gate
that already runs, and the two judgements a person actually makes — is this one
capability, and may it be published — are the only two this document hands to
anyone.
Motivation
Every projection of a capability is generated (HIP-0139 §1), so the expensive part of adding one is never the code. It is the name, the address and the boundary. A name that is not the word people say is carried into nine places at once. An address that belongs to another capability inherits that capability's audience. Both cost a paragraph to fix before the first commit and a coordinated rename after a client has been generated against them.
So the review happens where it is cheap: on a document with a name in it. The grounds for refusing one are short enough to check in a sitting.
Specification
The key words MUST, MUST NOT, SHOULD, SHOULD NOT and MAY are to be interpreted as in RFC 2119.
§1 The number
Numbers are allocated in two ranges.
- 0000–0999 — how we work and what the platform is: the process, the conventions, the principles, the service standards, the operator's resources.
- 1000 and above — one HIP per capability. A HIP in this range specifies
exactly one capability and declares it in front matter,
capability: <name>, from the moment the cloud serves that name (§2.7).
A new capability HIP takes the next free number at 1000 or above, chosen so the capability sits beside the ones it is read with. A number is never reused and a HIP is never renumbered: the number is how a reader cites the document. A capability HIP that already holds a number below 1000 keeps it — the range decides where a new one goes, not where an existing one lives.
How many capabilities there are, and how many carry a HIP, are measured and not
written down. python3 scripts/coverage.py --report prints both against the
release it is run on.
§2 From a gap to a served capability
The order is spec first (HIP-0139 §6): write the HIP, land the code that implements it, let every projection regenerate. Each step is proved by a gate that already runs.
- Name it. HIP-0139 §2 decides the name and nobody else does. A name that fails it is refused here, at the title, rather than at the directory.
- Check nobody owns it. A capability that already answers for the thing is
its owner, and what is being proposed is an expansion of theirs (§6). The
served document is the authority for what is answered;
python3 scripts/coverage.py --reportis the authority for which HIP covers which name. - Write the HIP. A number from §1,
requires: HIP-0139, and each thing HIP-0139 §6 requires. While nothing is served the front matter carries nocapability:key — that key declares a name the cloud answers, andcoverage.pyCV006 refuses a declaration nothing serves — and the Abstract says, in those words, that the capability is proposed and serves nothing today. - Merge it.
python3 scripts/lint-hips.pyandpython3 scripts/coverage.pyboth pass. From here the document is the specification and the code is what disagrees with it. - Land the code.
apps/<name>inhanzoai/cloudreturning a*zip.App,plugin/<name>/main.go, and one manifest row naming/v1/<name>.plugin/gen-app-cmdsproves the bijection.openapi/misfiled.txtproves the address, and it may not gain a line, so a new capability's address is right in its first commit or the commit does not land. - Group it. The name joins one of the nine domains in
hanzoai/openapicapabilities.yaml.publish.pyrefuses a served name grouped nowhere and a grouped name nothing serves. - Declare it. The change that puts the name in the taxonomy is matched by a
change here that adds
capability: <name>to the HIP. Both halves ofcoverage.pythen agree: a served name with exactly one HIP, and a declaration of a name that is served.
Steps 5 to 7 leave the capability at alpha (§4). No line is ever added to
capability-coverage.txt on this path (§5).
§3 What a proposal must contain
A proposal is reviewable when a reader can check every claim in it without asking the author a question. It states:
- the name, and why it is the word people say (HIP-0139 §2);
- the address
/v1/<name>and the operations under it — each typed, or declared with prose and the reason the response cannot be a value; - the boundary: the one store it owns, or that it owns none;
- the nearest capability that already exists, and why this surface is not theirs;
- everything HIP-0139 §6 requires of a capability HIP — tenancy, money, events, telemetry, stage, upstream, and what an attacker gets from the wrong implementation;
- what is served today, honestly. A proposal that serves nothing says so. A surface written in the present tense is a claim a reviewer will check and a promise the corpus will carry.
It does not restate HIP-0139 §1–§5, and it names no count a gate measures.
§4 Stage
Stage is a fact about the product; status is a fact about the text (HIP-0139
§8). They move independently: a ga capability may carry a Draft HIP, and a
Final HIP may specify an alpha one.
alpha. The entry. The manifest row declares it, the prefix answers 404 to
every org that does not hold the flag named for the capability (/v1/flags, key
<name>), and no generated client, tool list, command group or public page
carries it. Evidence to enter: the merged HIP, and one route that answers. What
alpha buys is that the shape of the surface may still change without breaking
a caller. It does not buy an unsettled address, and it does not suspend a gate —
HIP-0139 §3 and §8.4 bind at every stage.
beta. A customer may be let in. Evidence:
- a request that carries no org is refused, and the store is opened by org, so a flag admits a tenant and never a reader of everyone's rows;
- the price is declared in the surface — free, said in those words, or a priced route — because from the first admitted org the code bills whatever it says and the HIP is the promise it is measured against;
- every event the HIP names is published on the bus, and every span, metric and log line it names is emitted. A promise nobody has watched fire is not evidence;
- an org that did not build the thing holds the flag and has called it.
ga. The promotion publishes. It is one edit to the manifest row (HIP-0139
§8.4), and from the next release the operations are in the public document,
<Name>Api is in every generated client, the tool is listed at /v1/mcp,
hanzo <name> is a command group and docs.hanzo.ai/<name> is a page. Evidence:
- no line in
openapi/misfiled.txtnames the capability — its address is its name; - every operation the public rule will publish is typed (HIP-0139 §4.2, §4.3): a generated client cannot express one that is not;
- the name is grouped in
capabilities.yaml; coverage.pyfinds exactly one HIP declaring it, and that HIP states each thing HIP-0139 §6 requires — the price and the tenancy refusal above all, because atgaboth become public promises;openapi/floor.jsoncarries the capability's operation count as of the promotion. From then on the surface may not shrink unless the commit that shrinks it says so.
At ga the flag named for the capability admits nobody in particular; the
prefix answers every org.
There is no demotion. Taking a published surface back is a removal — operations
leaving the document, generated clients losing methods — and floor.json is
what makes that removal something a commit has to say out loud.
§5 The ratchet
capability-coverage.txt carries the capabilities that have no HIP. It exists
for one case: a capability reaches the taxonomy before its specification is
written. The name is added there in that same change, so coverage.py CV001
does not fire, and the line is deleted in the same commit as the HIP that
specifies it — CV002 fails on a line whose capability is now declared, so
neither half can rot.
The file may only shrink. A name that leaves it never returns, because what removed it was the HIP that specifies it, and a name enters it exactly once. A capability proposed by §2 never appears in the file at all: its HIP is merged before its first route answers, and the declaration lands with the name.
§6 Expanding a capability that exists
The HIP is amended first, in the order §2 states: amend, land, regenerate.
- A new operation. Amend the capability's address section to name it and
its type, then land it.
floor.jsonrises.misfiled.txtmay not gain a line, so the route is under/v1/<name>. The operation is typed, or declared with the prose HIP-0139 §4.3 requires. - A new address. There is none. A capability answers at
/v1/<name>for its whole life (HIP-0139 §3.1) and what grows is the tree beneath it. A proposal for a second top-level address is a proposal for a second capability, and is written as one: §2 from the top, its own name, its own number, its own HIP. - A widened surface. An operation entering the public document, a price changing, an event name being published, a store gaining rows a customer can read back: each is a promise, and the promise is written in the HIP before it is made. A stage promotion is the widest of them and takes the evidence in §4.
- A capability carved out of one that exists. Permitted along a store boundary and nowhere else (HIP-0139 §7.2). The new capability gets its own HIP; the amendment that gives up the surface lands with it, so no interval exists in which two HIPs claim one address.
An amendment never needs a new HIP. A capability's HIP is the living document
for that capability: it is amended in place, and its status: says how settled
the text is.
§7 Grounds for refusal
A proposal is refused, and the refusal names which of these it is:
- The name breaks HIP-0139 §2 — a compound word, a hyphen, an underscore, the plural of a capability that already exists in the singular, or an abbreviation that is not the word people say.
- The surface is already owned. Another capability answers for the thing proposed. The remedy is §6.1: an operation under the owner's address.
- There is no boundary. The proposal owns no store and does nothing another capability's routes do not already do. A handful of routes over somebody else's store is that capability's routes.
- It wants a second top-level address (§6.2).
- It is two capabilities. One HIP declares one capability;
coverage.pyCV005 refuses the second declaration, and two specifications in one file are unreadable well before a gate sees them. - It is written in the present tense about something that serves nothing. A reviewer weighs a claim by checking it. This one is refused for being uncheckable, not for being wrong.
Refusals 1, 4 and 5 are mechanical, and the gates would catch each of them. Catching them in the paragraph is cheaper than catching them in a commit that has to be reverted across nine projections.
§8 Who decides
Two things are decided by no one. The name is decided by HIP-0139 §2 and the address by HIP-0139 §3, and the gates HIP-0139 §5 lists refuse a projection that disagrees. There is no exception to grant, because an exception would have to be a line in a ratchet and every ratchet here only shrinks.
Everything else is decided by the HIP editors, on the merge: whether the proposal is one capability, whether its boundary is real, and whether the corpus already covers it. The states are the corpus's — Draft, Review, Last Call, Final — and a status says how settled the text is, never whether a customer is shown the thing.
The author may enter alpha alone. It costs a directory, a manifest row and a
flag, and no customer who does not hold the flag can tell it exists.
ga is the editors', on the evidence in §4. It is the one decision that
publishes: afterwards the surface is in every generated client and held by
floor.json, which makes it the one decision that is expensive to take back.
Rationale
The alternative is to let a capability arrive as code and write its HIP
afterwards. That case has a file — capability-coverage.txt — and the file is
the argument against it: every line is a name already living in nine projections
whose specification is still owed, and the name is the part that was free to
change before the first commit. Spec first is not ceremony. It moves the
irreversible decision to the one moment it is still reversible.
The alternative to a declared stage is a flag and a convention about what the flag means. Then every reader has to ask what a route is for, and the answer lives with whoever remembers. A stage in the manifest row is an answer the public rule can read.
Security Considerations
Two steps here are the security boundary; the rest is bookkeeping.
The address decides the audience. A route admitted under another capability's
prefix inherits that capability's audience and its public rule — an operator's
view offered as a customer method is the concrete case (HIP-0139 Security
Considerations). §2's ordering closes it: the address is proved by
misfiled.txt before any projection publishes the operation.
The stage decides who learns the capability exists. HIP-0139 §8.2 fixes that
answer at 404 and never 403, because 403 tells an outsider the name is real. A
capability that reaches beta without the tenancy refusal in §4 is worse than
one that never shipped: the flag admits an org, and then the store decides
nothing.
References
- HIP-0000 — Hanzo AI Architecture & Framework
- HIP-0026 — Identity and Access Management
- HIP-0106 — The Hanzo Plugin Contract
- HIP-0135 — What Is Public
- HIP-0139 — Capability
Copyright
Released under CC0 1.0 Universal Public Domain Dedication.