A feature is the overlay's business construct: a way to package capabilities for consumption. Where a capability is the engineering primitive — what the system can do — a feature is how that capability is presented to an audience: who it's for, how mature it is, and, when it's paid, what it costs to unlock. You build capabilities first; you package them into features.
A feature lives on the system that ships it, in its feature {} block:
feature "Product catalog" {
consumer "operator"
maturity "beta"
category "catalog"
description "Create a product (born DRAFT), price it, and read it back by id or as a list."
value 8
}That one feature packages two engineering capabilities — ProductCatalogManagement and ProductBrowsing — into a single operator-facing product. The capabilities are what got built; the feature is how they're sold to the operator audience.
Capability vs feature — the layering
This is the distinction to hold onto:
capability | feature | |
|---|---|---|
| Kind of thing | engineering construct | business construct |
| Answers | what the system can do | how a capability is packaged for consumption |
| You… | build it | sell / present it |
| Lives | a first-class capability node | a feature {} block on a system |
| Segmented by | its consumers (personas) | consumer + maturity (the audience + stage) |
They compose: capabilities are the layer underneath, features the packaging on top. A free feature's relationship to its capabilities is descriptive — the feature describes the bundle. A paid feature makes the relationship a hard link, which is where entitlement comes in.
Entitlement — packaging a capability behind a plan
When a feature is gated behind a plan, four fields turn it from a description into a real access rule:
feature "Priority checkout" {
consumer "customer"
maturity "planned"
category "ordering"
description "A jump-the-queue checkout lane for subscribers on the pro plan."
value 6
requires_tier "pro"
requires_capability OrderCheckout
entitlement_key "chowk.ordering.priority-checkout"
error NotEntitled
}requires_tiernames the plan that unlocks the feature.requires_capabilitynames the first-class capability node the feature gates. This must be a declaredcapability— not a free-form string — which is exactly the layering made enforceable: the business package points at the engineering primitive it bundles.entitlement_keynames the business key the generated guard actually looks for in the caller's entitlement bundle. See the warning below — this is not optional in practice.errornames the typed error raised when the caller is not entitled.
From these, the framework generates the app-side entitlement guard on every operation that implements the feature — you declare the policy; the framework enforces it. "Priority checkout" is the business packaging of the OrderCheckout capability, sold on the pro plan.
⚠️ requires_capability and entitlement_key are two different identities — declare both
It is tempting to read requires_capability as "the thing the guard checks". It is not, and getting this wrong locks every customer out of the feature — including the ones who paid for it.
The two names answer different questions, and they live in different namespaces:
| Field | Names | Namespace | Used by |
|---|---|---|---|
requires_capability | the engineering capability node | a declared capability, PascalCase FQN (OrderCheckout) | the architecture graph — the demand/supply link between the feature and the capability that realizes it |
entitlement_key | the business entitlement | a lowercase namespaced key (chowk.ordering.priority-checkout) | the generated runtime guard — the key it looks up in the caller's entitlement bundle |
A subscription plan sells a business key. The capability FQN is PascalCase and exists only in your model, so no subscription token can ever carry it. Omit entitlement_key and the guard falls back to keying on the FQN — a key that is never present in any real bundle — so every caller fails the check and receives 403 ENTITLEMENT_REQUIRED, on every plan, including the highest tier.
The failure is silent at author time. A feature that declares requires_capability with no entitlement_key parses and validates with no error and no warning — the model is structurally fine, and the architecture graph even reconciles correctly. The mistake surfaces only as a uniform 403 in a running system, which reads like a broken subscription rather than a missing line in the model. Whenever you write requires_capability, write entitlement_key beside it, and make sure its value matches the key your plan catalog actually grants.
The other half: implements on the verbs
A feature declared on a system is the plan. The built reality is a verb — a command, a query, a route — declaring that it implements that feature. Chowk's ordering routes attribute themselves to the "Order lifecycle" feature and, at the same time, to the REST access surface:
post "/orders" : PlaceOrder systems [OrderingSystem]
implements "Order lifecycle" for customer maturity "beta"
implements OrderingApiTwo kinds of implements sit on that one line, distinguished by their argument:
implements "Order lifecycle" for <consumer> maturity "…"— a feature link (a quoted feature name + the audience it's realized for). This is what reconciles the plan against the build.implements OrderingApi— a UI-surface link (a bare surface name). See UI surfaces.
The capability inventory and reconciliation
Planned features (on systems) and implemented features (on verbs) are matched by (feature, consumer), and the result is rendered as the capability inventory in the architecture output — the full catalogue of what each audience can do, segmented by consumer and maturity, with the maturity reconciled to the shipped reality rather than the planned stage.
Two reconciliation outcomes matter:
- Planned but not implemented — a feature a system plans that no verb yet realizes. This is the roadmap: informational, expected.
- Implemented but not planned — a verb that implements a feature no ancestor system planned. This is drift: a warning, because a capability shipped without a home in the plan is a capability the architecture map doesn't know it has. The fix is to add the feature to the owning system — reconciling the built reality back into the plan.