# Write a feat or a stack {#root}

@status:doc/work @audience:author

[p01] A feat says what to build without saying how; a stack says how a technology does it. This page writes one of each and connects them through the abilities one needs and the other provides.

[p02]
```prompt
Create two in-tree packages under vibevm/vibepacks/ in the current VibeVM project. The first is a feat org.acme/welcome-page that describes a welcome page with acceptance criteria and requires the capability ui:page-host. The second is a stack org.acme/static-site that provides ui:page-host and explains how a page is built as a static HTML file. Run vibe check on both.
```

- needs: the vibevm skill installed for your agent; a project with `vibe.toml` at the root

outcome: the feat's manifest requires `ui:page-host`, the stack's manifest provides it, each has its specification documents under `vibevm/vibespecs/`, and `vibe check` reports no errors for either

- assert: `vibe check --path vibevm/vibepacks/org.acme/welcome-page/v0.1.0 --quiet`
- assert: `vibe check --path vibevm/vibepacks/org.acme/static-site/v0.1.0 --quiet`

## What happens {#what-happens}

[p03] The agent creates both slots with `vibe init package`, sets their kinds in the [manifests](../glossary/index.xml#manifest), writes the feat's specification, acceptance criteria and [capability](../glossary/index.xml#capability) requirement, writes the stack's description, conventions and the capability it provides, and checks each. When a project later installs the feat, the resolver looks for a package providing `ui:page-host` among the project's stacks; the stack satisfies it, and the two are matched without either naming the other.

> [p04] `[[registry]]` is an **array**, priority-ordered. `[[mirror]]` is a first-class fallback layer, transparent to the lockfile. `[[override]]` bypasses the resolver for pins. Schema and code path support all three from day one.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#SHAPE-REGISTRY-ARRAY>

## A feat {#a-feat}

[p05] A feat describes what a feature does for its user, in terms any stack can implement: purpose, behaviour, acceptance criteria, data it needs, what happens when things go wrong. It never names a framework. Its [manifest](../glossary/index.xml#manifest) declares the abilities it needs from a stack as capabilities, `namespace:name@constraint`, and nothing else about technology.

[p06]
| Path | Purpose |
| --- | --- |
| `vibevm/vibespecs/feats/<name>/SPEC.md` | what the feature does, for whom, and why |
| `vibevm/vibespecs/feats/<name>/acceptance.md` | observable criteria a build must meet |
| `vibevm/vibespecs/feats/<name>/data-model.md`, `api.md`, `ui-flows.md`, `failure-modes.md` | the parts that apply, one topic per file |

[p07] In the manifest: `[requires] capabilities = ["ui:page-host@^1"]` and, for a feat that needs a stack at all, `[compatibility] requires_kinds = ["stack"]`.

## A stack {#a-stack}

[p08] A stack is a technology context: it says how the abstract abilities a feat asks for are realised with one set of tools, and it may bind the [lifecycle](../glossary/index.xml#lifecycle)'s build and test [phases](../glossary/index.xml#phase) to that toolchain. Its manifest declares what it provides, `[provides] capabilities = ["ui:page-host@1.0"]`, and its specification documents describe each provided capability in a file of its own, plus conventions, tooling and deployment.

[p09]
| Path | Purpose |
| --- | --- |
| `vibevm/vibespecs/stacks/<name>/STACK.md` | what the stack is and when to choose it |
| `vibevm/vibespecs/stacks/<name>/capabilities/<capability>.md` | one file per provided ability: how it is realised |
| `vibevm/vibespecs/stacks/<name>/conventions.md`, `tooling.md`, `deployment.md` | naming and layout, the build and test commands, how a build ships |
| `vibevm/vibespecs/boot/<name>.xml` | an optional snippet that surfaces the active stack at session start |

[p10] A stack may also bind lifecycle [contributions](../glossary/index.xml#contribution) in its manifest, so that `vibe build` and `vibe test` in a consuming project run the stack's toolchain without further configuration.

> [p11] A stack package may ship its preset as a set of `[[extension]]` contributions in its own manifest (auto, trust-gated like everything else) — this is how `rust-ai-native-lang` teaches `vibe build`/`vibe test` to drive cargo without vibe hardcoding cargo, the OOS-AUTODETECT posture of PROP-024 kept intact: the package declares, vibe never infers.
>
> <spec://org.vibevm.core/vibevm/common/PROP-054#STACK-CONTRIBUTES-PRESET>

## Capabilities {#capabilities}

[p12] A capability is an abstract interface: a namespace, a colon, a name, and optionally a [version constraint](../glossary/index.xml#version-constraint). A feat requires; a stack provides; the resolver matches them at install time, and refuses a project whose feats need an ability no installed stack provides. Choose names by what the ability does for the feature, not by the technology: `ui:page-host`, `db:relational`, `auth:oauth-callback`.

> [p13] **Decision.** A package's identity is the tuple `(kind, name, version, content_hash)`. The `content_hash` is a digest over the deterministically-ordered concatenation of `(rel_path_bytes || 0x00 || file_bytes || 0x00)` for every file in the package directory, and **the value names the recipe that produced it** ([PROP-044 §4.7](../../common/PROP-044-change-native-formats.xml#machinery)): `sha256-tree/1:<hex>` is recipe 1, whose exclusion list, path normalisation and traversal order are carried as data in `formats/hash_recipes/1.toml`; the bare `sha256:<hex>` is recipe 0, the pre-recipe form, frozen verbatim in code — not configurable, because a frozen recipe that can be edited is not frozen — so that values written before recipes were named stay readable. Two hashes are comparable only **at the same recipe**; comparing across recipes answers a question nobody asked, and is never done silently. [PROP-024 §2.2](../../common/PROP-024-code-bearing-packages.xml#shippable-tree) re-scopes this to the package's **shippable tree** — its source, minus build output (`.git/`, `.vibe/`, `target/`, `node_modules/`, `.vibeignore` globs) — so a code-bearing package's identity is its source, not its build state; that exclusion lands with the code that implements it. The URL used to fetch the content is **informational** — recorded in the lockfile for debuggability, not for identity.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#IDENTITY-TUPLE>

## Edge cases and rules {#edge-cases}

[p14] The word `stack` also names a [family](../glossary/index.xml#family) bundle: a package of kind `stack` with nothing but exact pins of a language family's members. Both are stacks in the register; the description tells them apart.

> [p15] **`<family>`** — the *aggregator*. `kind = "stack"`, content-minimal: a
>   `vibe.toml` and a `README.md`, and nothing else — no code, no boot snippet,
>   no `specmap.toml` / `conform.toml`. Its whole job is to name the family's
>   members at one resolved version set through exact `=X.Y.Z` pins in
>   `[requires]`. Requiring the aggregator installs the family.
>
> <spec://org.vibevm.core/vibevm/common/PROP-028#ROLE-AGGREGATOR>

[p16] A feat's acceptance criteria are what the agent checks after a build; write them as observable [facts](../glossary/index.xml#fact), not as wishes.

[p17] Two feats requiring the same capability may be satisfied by one stack; a project with several stacks marks one as active for a build.

