# Написать feat или stack {#root}

@status:doc/work @audience:author

[p01] Feat говорит, что построить, не говоря как; stack говорит, как это делает технология. Эта страница пишет по одному и соединяет их через умения, которые одному нужны, а другой предоставляет.

[p02]
```prompt
Создай два пакета в дереве под vibevm/vibepacks/ в текущем проекте VibeVM. Первый — feat org.acme/welcome-page, который описывает страницу приветствия с критериями приёмки и требует возможность ui:page-host. Второй — stack org.acme/static-site, который предоставляет ui:page-host и объясняет, как страница собирается статическим HTML-файлом. Запусти vibe check на обоих.
```

- needs: навык vibevm, установленный у вашего агента; проект с `vibe.toml` в корне

outcome: манифест feat требует `ui:page-host`, манифест stack его предоставляет, у каждого есть документы спецификации под `vibevm/vibespecs/`, и `vibe check` не находит ошибок ни у одного

- 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}

[p03] Агент создаёт оба слота через `vibe init package`, задаёт их виды в [манифестах](../glossary/index.xml#manifest), пишет спецификацию feat, критерии приёмки и требование [возможности](../glossary/index.xml#capability), пишет описание stack, его соглашения и возможность, которую он предоставляет, и проверяет каждый. Когда проект позже ставит feat, резолвер ищет пакет, предоставляющий `ui:page-host`, среди stack-пакетов проекта; stack ему подходит, и двое сопоставляются, хотя ни один не называет другого.

> [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>

## Feat {#a-feat}

[p05] Feat описывает, что фича делает для своего пользователя, в терминах, которые может реализовать любой stack: назначение, поведение, критерии приёмки, нужные данные, что происходит, когда что-то идёт не так. Он никогда не называет фреймворк. Его [манифест](../glossary/index.xml#manifest) объявляет умения, которые ему нужны от stack, как возможности, `namespace:name@constraint`, и больше ничего о технологии.

[p06]
| Путь | Назначение |
| --- | --- |
| `vibevm/vibespecs/feats/<name>/SPEC.md` | что фича делает, для кого и зачем |
| `vibevm/vibespecs/feats/<name>/acceptance.md` | наблюдаемые критерии, которым должна отвечать сборка |
| `vibevm/vibespecs/feats/<name>/data-model.md`, `api.md`, `ui-flows.md`, `failure-modes.md` | те части, что применимы, по одной теме на файл |

[p07] В манифесте: `[requires] capabilities = ["ui:page-host@^1"]` и, для feat, которому вообще нужен stack, `[compatibility] requires_kinds = ["stack"]`.

## Stack {#a-stack}

[p08] Stack — технологический контекст: он говорит, как абстрактные умения, о которых просит feat, реализуются одним набором инструментов, и может привязать [фазы](../glossary/index.xml#phase) сборки и тестирования [жизненного цикла](../glossary/index.xml#lifecycle) к этому тулчейну. Его манифест объявляет, что он предоставляет, `[provides] capabilities = ["ui:page-host@1.0"]`, а документы спецификации описывают каждую предоставляемую возможность в отдельном файле, плюс соглашения, инструменты и выкладку.

[p09]
| Путь | Назначение |
| --- | --- |
| `vibevm/vibespecs/stacks/<name>/STACK.md` | что такое этот stack и когда его выбирать |
| `vibevm/vibespecs/stacks/<name>/capabilities/<capability>.md` | по файлу на предоставляемое умение: как оно реализовано |
| `vibevm/vibespecs/stacks/<name>/conventions.md`, `tooling.md`, `deployment.md` | именование и раскладка, команды сборки и тестов, как сборка поставляется |
| `vibevm/vibespecs/boot/<name>.xml` | необязательный фрагмент, который показывает активный stack на старте сессии |

[p10] Stack может также привязать в манифесте [вклады](../glossary/index.xml#contribution) жизненного цикла, чтобы `vibe build` и `vibe test` в проекте-потребителе запускали тулчейн stack без дополнительной настройки.

> [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}

[p12] Возможность — абстрактный интерфейс: пространство имён, двоеточие, имя и, необязательно, [ограничение версии](../glossary/index.xml#version-constraint). Feat требует; stack предоставляет; резолвер сопоставляет их при установке и отказывает проекту, чьим feat нужно умение, которого не предоставляет ни один установленный stack. Выбирайте имена по тому, что умение делает для фичи, а не по технологии: `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}

[p14] Слово `stack` называет и бандл [семейства](../glossary/index.xml#family): пакет вида `stack`, в котором нет ничего, кроме точных пинов участников языкового семейства. В реестре и то и другое stack; различает их описание.

> [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] Критерии приёмки feat — то, что агент проверяет после сборки; пишите их как наблюдаемые [факты](../glossary/index.xml#fact), а не как пожелания.

[p17] Два feat, требующие одну возможность, могут быть удовлетворены одним stack; проект с несколькими stack помечает один как активный для сборки.

