# Пишите спецификации, которые агент может процитировать {#root}

@status:doc/work @audience:author,agent

[p01] Текст в пакете полезен агенту, только если у каждого правила в нём есть адрес. Эта страница показывает форму такого текста: именованные разделы, одна мысль на единицу, статус на каждой и адрес, который после публикации не меняется никогда.

## Зачем адрес {#two-processes}

[p02] Человек и агент делят один репозиторий и ничего больше: ни коридора, ни общей памяти, ни интонации. Дерево [спецификаций](../glossary/index.xml#specification) — единственный канал между ними, а канал работает, когда на сообщение можно указать. «Поправь число повторов» отправляет агента гадать; «поправь `spec://org.acme/notes-flow/flows/notes/PROTOCOL#RETRY-COUNT`» отправляет его к одной строке. Второе стоит около двадцати токенов; первое стоит сотни и может попасть не в то правило.

> [p03] <spec://org.vibevm.world/addressable-specs/flows/addressable-specs/ADDRESSABLE-SPECS-PROTOCOL#THE-SPEC-TREE-IS-THE-ONLY-CHANNEL>

> [p04] <spec://org.vibevm.world/addressable-specs/flows/addressable-specs/ADDRESSABLE-SPECS-PROTOCOL#FOR-POINT-CORRECTIONS-THE-URI-WINS>

## Адрес {#the-address}

[p05] `spec://<group>/<name>[@<version>]/<path>/<document>#<anchor>`: [координата](../glossary/index.xml#coordinate) пакета, необязательная версия, путь документа внутри `vibevm/vibespecs/` без расширения и [якорь](../glossary/index.xml#anchor). Версия — удобство, а не обязанность: без неё адрес разрешается по самой свежей установленной версии. Якоря — это идентификаторы разделов и [фактов](../glossary/index.xml#fact) в одном адресном пространстве, так что правило цитируется одинаково, будь оно разделом или одним предложением.

> [p06] <spec://org.vibevm.world/addressable-specs/flows/addressable-specs/ADDRESSABLE-SPECS-PROTOCOL#URI-SCHEME-IS-THE-FULL-GRAMMAR>

> [p07] `@<version>` is **optional — a feature, never an obligation (owner-ruled 2026-08-04, B-028: «указание версий — опциональная фича; если версия не указана — используется самая свежая»)**; absent, the address resolves against the **freshest installed version** of the package (semver-newest among the materialised `vibedeps/` slots — the one deterministic offline reading of «самая свежая»). Several coexisting slots are therefore not an ambiguity but an ordered set with a defined maximum; an explicit `@<version>` still picks its exact slot, including a non-newest one.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-035#URI-VERSION-OPTIONAL>

[p08] Example `explain` is copied from the source page at projection time.

## Единица {#the-unit}

[p09] Единица — один заголовок с якорем и текст под ним до следующего заголовка; она несёт одно решение или одно правило, понятна сама по себе и помещается на страницу. Если единице нужно «а ещё», это две единицы. Контрактные утверждения используют MUST, SHOULD и MAY; читатель никогда не должен гадать, обязывает ли предложение. Проверяемые утверждения стоят вне блоков кода, потому что у блока нет якоря, а инструкция внутри него не проверена по построению.

> [p10] <spec://org.vibevm.world/addressable-specs/flows/addressable-specs/authoring-rules#ONE-UNIT-CARRIES-ONE-DECISION>

> [p11] <spec://org.vibevm.world/addressable-specs/flows/addressable-specs/authoring-rules#CONTRACT-STATEMENTS-USE-RFC-2119-VERBS>

> [p12] <spec://org.vibevm.world/addressable-specs/flows/addressable-specs/authoring-rules#A-CHECKABLE-CLAIM-BELONGS-OUTSIDE-THE-FENCE>

## Две сериализации, одна модель {#the-dialect}

[p13] Спецификация пишется в Markdown или на XML-диалекте проекта, и обе формы разбираются в одну модель документа. В XML раздел — элемент, названный по своему якорю, `<retry-policy title="3. Повторы">`. Правило — элемент, названный по своему идентификатору, с атрибутом `fact="true"` и статусом. В Markdown то же правило — абзац, который открывается `@fact:RETRY-COUNT` и закрывается `@status:spec/done`. Инструмент, который ничего не знает о вашем словаре, всё равно находит каждое правило одной проверкой атрибута, а конвертер переводит одну форму в другую, сообщая обо всём, что не пережило бы обратный путь.

> [p14] **Decision (ADR-part; owner ruling 2026-08-22,
> verbatim): «сконвертируй и факты тоже. Предлагаю такой формат
> `<fact-name fact="true" ...>`. Таким образом кастомный XML-парсер всегда
> может найти соответствующие элементы».** A fact serialises with its ID
> as the element name, carrying the DISCRIMINATOR attribute —
> `<THE-LAW fact="true" status="impl/done">body</THE-LAW>` — so a reader
> that knows nothing of the vocabulary still finds every fact by one
> attribute test. The recognition law: an element IS a fact iff its name
> is `fact` (the generic form, which stays in the dialect) or it carries
> `fact="true"`. The named form is emitted whenever the id passes the
> same elementability predicate sections use (fact-id grammar already
> forbids leading digits, so the fallback tail is vocabulary collisions
> only); the typed-fact fence binding stays by id and does not change.
> The owner's second clause binds the scanners: the progress machinery
> must work when a fact's SOURCE — not a materialised copy — is authored
> XML; the host lane holds by construction (XML sources enter progress
> through the canonical MD projection) and is PINNED by explicit tests
> (an observed .xml source scans unit-for-unit equal to its MD twin),
> while the specmap engine's native reader learns the named form
> mirror-wise. The converter recipe bumps again (`specdoc/2` →
> `specdoc/3`); the host re-materialises once, after both shapes land.
> The owner's third clause (2026-08-22, same sitting) binds the boot
> lanes: «статические и динамические лоадеры должны хорошо работать с
> новым синтаксисом фактов» — pinned at the transition's landing by (a)
> the static-splice determinism test running over a NAMED-shape snippet
> whose projected facts survive into STATIC, (b) the vibe-spec
> normal-closure byte-equality test running over BOTH serialisations
> (generic and named) of one dependency, and (c) the polygon re-run at
> specdoc/3, whose control package auto-adopts the named shape through
> to_xml — INDEX targets, STATIC splice and every machine loader then
> exercise the final syntax end-to-end; the agent half of the dynamic
> router is §5a's measurement, deliberately run AFTER this transition so
> it measures the shape that ships. Landed: the recipe is `specdoc/3`,
> the host's 37 slots re-materialised once with named facts live (the
> redbook README golden pins 45), the recognition law holds in both
> readers with `fact="false"` a loud error, progress holds full
> ParsedDoc parity between an XML source and its hand-pinned MD twin
> across two scans, and pins (a)–(c) are in the tree — the splice
> snippet ships `<BOOT-RULE fact="true">`, the normal closure compiles
> three lanes byte-equal, the polygon re-ran 3/3. A live lesson worth
> its line: XML reserves every case-insensitive `xml`-prefixed name, so
> `XMLBOOT` cannot be an element — the predicate refuses it and the
> generic form carries such ids.
>
> <spec://org.vibevm.core/vibevm/common/PROP-045#NAMED-FACT-ELEMENTS>

> [p15] **Decision (ADR-part).** The XML dialect
> is deliberately ISOMORPHIC to the Markdown-expressible structure — exactly
> the constructs the markup contract names, in XML syntax, and nothing more.
> A schema-foreign element or attribute is a loud parse error, never a
> silent skip (the same closed-vocabulary law the typed-fact grammar took).
> This is what makes the owner's degradation law hold by construction:
> XML→MD loses nothing semantic because the dialect cannot express what MD
> cannot; «всё невыразимое — не поддерживается» is enforced by the schema,
> not by a lossy converter. **Reopened once, 2026-09-11, for exactly one
> genre** — §7 `##DOC-VOCAB-REOPENING`: the documentation vocabulary of
> PROP-057 is additive, gated by the package kind `doc`, and one-way to
> Markdown by law; for the spec vocabulary this decision stands unchanged.
>
> <spec://org.vibevm.core/vibevm/common/PROP-045#XML-DIALECT-IS-THE-MD-SUBSET>

> [p16] **Decision (ADR-part; owner ruling 2026-08-22,
> verbatim): «Гораздо логичней `<three-bands title=\"…\">`. … Вся суть XML
> нотации в том, что у тебя названия тэгов несут названия сущностей, это
> упрощает работу нейросети».** A section serialises with its ANCHOR as the
> element name — `<three-bands title="1. The three bands">` — because the
> dialect's first reader is an agent, and a tag that names its entity is
> self-describing where an endless `<section>` river is not. The generic
> form `<section id="…" title="…">` remains in the dialect as the REQUIRED
> fallback for the two cases XML itself forbids or the grammar reserves: an
> anchor that is not a valid XML name (leading digit) and an anchor
> colliding with the structural vocabulary (`spec`,`title`,`status`,
> `section`,`p`,`fact`,`list`,`item`,`table`,`tr`,`td`,`fence`,`quote») —
> measured over the live corpus, that tail is 2 anchors of 1393; the
> emitter writes the named form everywhere else, the readers accept both.
> The converter recipe bumps (`specdoc/1` → `specdoc/2`), so every
> transformed slot re-materialises by the derived-manifest law rather than
> lingering in the old shape. The owner's next call arrived the same
> day — facts follow, see `##NAMED-FACT-ELEMENTS`. Landed: the emitter
> writes named sections everywhere the predicate allows (the live redbook
> README golden carries 7 named / 0 generic), both readers accept both
> forms, and the engine mirrors the predicate verbatim across the
> separability seam.
>
> <spec://org.vibevm.core/vibevm/common/PROP-045#NAMED-SECTION-ELEMENTS>

> [p17] **Decision (ADR-part; owner ruling 2026-08-22,
> near-verbatim): «Не правильней ли не включать внутри list элементы item,
> а сразу ставить в тело list элементы типа `<THE-LAW fact="true"...>`? И
> вместо `<list>` использовать тэг `<facts>` — это новое слово для
> зарезервированного словарика… Если же список состоит из обычного текста
> (там могут даже иногда встречаться факты), то все элементы — это item и
> группировка — list, а факты в нём рендерятся как сейчас».** The law: a
> list whose every item is exactly one meaningful fact materialises as the
> vocabulary element `<facts ordered="…">` with the fact elements (named
> or generic) directly in its body — no `<item>` wrappers; any other list
> (plain text, or mixed with occasional facts) keeps today's
> `<list>`/`<item>` shape with facts rendered inside items. `facts` joins
> the reserved vocabulary (an anchor named `facts` falls back to the
> generic form); `ordered` carries over exactly as on `<list>`; the model
> is unchanged — both shapes parse to the same `Block::List`, so the
> reader accepts BOTH forms (old materialisations in the wild stay
> readable) and a rewrite normalises the all-fact shape to `<facts>`.
> Loud errors guard the grouping: a non-fact child inside `<facts>`, bare
> text inside `<facts>`, an empty `<facts>`. Both readers — the pivot and
> the specmap engine's native one — learn the form mirror-wise; the
> converter recipe bumps `specdoc/3` → `specdoc/4` and the host
> re-materialises once. Landed: the writer branch, the `facts_block`
> parser (split into the pivot's own `xml_facts.rs` along the engine's
> seam) and the vocabulary word sit in both readers with the four loud
> errors pinned; the engine proves model-identity by content hash
> between the two shapes; the redbook README golden re-pins with two
> `<facts>` groups and the same 45 named facts; the host's 37 slots
> re-materialised at `specdoc/4`; and the §5a stand re-ran as the
> regression tool it was left as — polygons rebuilt on the new shape
> (121 files carry groups), the sensitive tier (`gpt-5.5`@`low`) swept
> 9/9 with the negative control clean.
>
> <spec://org.vibevm.core/vibevm/common/PROP-045#FACTS-GROUP-ELEMENT>

[p18] Статус на каждой единице говорит, где она стоит: `spec/done` для устоявшегося правила, `impl/done`, когда оно уже воплощено в коде, `spec/work` для черновика. Два регистра идентификаторов несут сигнал бесплатно: идентификатор в верхнем регистре помечает правило с обязывающим весом, в нижнем — вводную или заметку.

> [p19] **Decision — two anchor-id registers (owner ruling, 2026-07-24).**
>      `@fact:UPPER-SLUG` names a **normative fact** (a law, rule, carrier,
>      changelog entry — content with binding weight); `@fact:kebab-case`
>      names a **service unit** (status lines, lead-ins, connective
>      prose).
>
> <spec://org.vibevm.core/vibevm/modules/vibe-facts/PROP-043#DECISION-TWO-REGISTERS>

[p20] Example `convert` is copied from the source page at projection time.

[p21] `vibe refactor convert-source --to xml` или `--to markdown` конвертирует файлы или целые папки, пропуская дерево зависимостей и сгенерированные файлы. Он пишет соседнюю форму и удаляет оригинал одним действием, так что дерево никогда не держит обе формы одного документа. Перед записью он конвертирует результат обратно и сравнивает: побайтно совпавший обратный путь конвертируется молча, а потеря комментариев или раскладки отвергается с описанием по каждому файлу, пока вы не подтвердите или не передадите `--force`. Изменение смысла отвергается всегда, потому что это дефект конвертера, а не вашего файла. `--dry-run` сообщает о каждом файле и ничего не пишет.

> [p22] `vibe refactor convert-source --to
> xml|markdown [PATH…]` converts spec sources between the two PROP-045
> serialisations (`md` accepted as an alias of `markdown`). Each PATH is a
> file or a directory; a directory walks recursively and selects spec
> sources (`is_spec_source`) currently in the OPPOSITE form; a file in the
> target form already is reported `already` and skipped. The walk always
> skips: `vibedeps/` (##VIBEDEPS-NEVER-CONVERTED), dot-directories,
> `target/`, and any file whose leading lines carry the `generated by
> vibe` marker (generated artifacts regenerate; they are not authored
> sources) — an EXPLICIT file argument overrides only the marker skip,
> never the `vibedeps/` skip.
>
> <spec://org.vibevm.core/vibevm/common/PROP-051#CONVERT-SOURCE-SURFACE>

> [p23] A conversion writes the sibling
> serialisation and deletes the original in the same act, so the
> one-document-one-form law (PROP-045 ##TARGET-MIXED, the pair-collision
> check) holds at every instant — the tree never holds `X.md` and `X.xml`
> together, not even transiently between files. The verb assumes version
> control underneath it and keeps no backups of its own.
>
> <spec://org.vibevm.core/vibevm/common/PROP-051#ONE-DOCUMENT-ONE-FORM-ON-CONVERT>

> [p24] The destructiveness check is the owner's law,
> implemented by reverse reconversion through the pivot: for a source
> `S`, parse to IR, emit the target form, read the target form back, and
> project it into the SOURCE form again; compare. Three classes:
> **(1) byte-stable** — the back-projection equals `S` byte-for-byte:
> converts silently. **(2) IR-stable loss** — the back-projection
> re-parses to the SAME IR but differs in bytes (dropped MD/XML comments,
> normalised layout): the verb REFUSES with a per-file description of
> what is lost, and proceeds only on interactive confirmation or
> `--force`. **(3) IR-divergent** — the back-projection re-parses to a
> DIFFERENT IR: always refused, `--force` does not apply; that class is a
> `vibe-specdoc` defect to file (the pivot broke its own round-trip law),
> never a corpus to damage.
>
> <spec://org.vibevm.core/vibevm/common/PROP-051#HONESTY-BY-REVERSE>

> [p25] On a TTY, class-2 files prompt per file
> (yes / no / all); off a TTY, class-2 without `--force` is an error
> listing every lossy file and what each loses. `--force` waives class 2
> only. `--dry-run` classifies and reports every file, writes nothing,
> and always exits 0 (an inventory, not an attempt); a real run exits 0
> iff every requested conversion landed, non-zero when any file was
> refused or declined.
>
> <spec://org.vibevm.core/vibevm/common/PROP-051#FORCE-AND-PROMPT>

## Директивы: use, embed и read {#directives}

[p26] Спецификация может подтянуть другую по адресу. `#use spec://…` подтягивает весь раздел верхнего уровня, содержащий адресованный узел, но не его соседей; `#embed` вклеивает ровно адресованный узел, не больше. Слияние по умолчанию — `:add`, так что текст интерфейса не нужно повторять, чтобы он попал в результат. Каждый файл, который называет директива, должен быть объявлен в [манифесте](../glossary/index.xml#manifest) пакета. Циклы законны в слое контрактов и запрещены между телами исходников, и именно это гарантирует, что сборка всегда заканчивается.

> [p27] **`#use` pulls the whole top-level anchored ancestor** of the addressed node — reference a subsection, load its enclosing top-level section as one connected block. It does **not** pull that ancestor's siblings; siblings are read only when themselves needed.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-035#USE-ANCESTOR-RULE>

> [p28] **`#embed` has arbitrary granularity** — it splices exactly the addressed node, no more.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-035#EMBED-EXACT-RULE>

> [p29] **Default is `:add`** (absent a `:`-suffix) — so the interface text need not be duplicated to appear in the result.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-035#MERGE-DEFAULT-ADD>

> [p30] Every file referenced by any directive MUST be declared in the package's `vibe.toml` (the directive graph and the manifest cannot disagree).
>
> <spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-035#DIRECTIVE-MANIFEST-AGREE>

> [p31] **Invariant.** The **contract layer is where cycles are legal; the source layer is where topological order is mandatory.** This is the theoretical no-deadlock guarantee: as long as the contract hierarchy is acyclic-under-`#embed` and no source body participates in a `#use` cycle, the build always terminates.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-035#NO-DEADLOCK-INVARIANT>

[p32] В прозе, которую читает агент, `@spec://…` с собакой обязателен: агент читает его на месте, один раз, при первой встрече, а компилятор никогда его не вклеивает. Голый `spec://…` остаётся на усмотрение агента.

> [p33] **`@spec://…`** (an `@` before `spec`) is an **in-place use**: the agent MUST read it (mandatory), exactly as if a `#use` had been declared at the top of the file. **Amendment (2026-08-24, the normal-flip ruling):** the obligation is the AGENT's, not the AOT compiler's — a `@spec` target is **never spliced** into a compiled lane; the address itself is the compiled artefact, and the §2 equivalence invariant is carried by the agent's read obligation. (Splicing every `@spec` realised ##OPEN-CLOSURE-EXPLOSION on the first normal-format host build: the lane grew 250 KB → 2.5 MB off prose pointers.) Only an explicit `#use` is a compiler splice edge.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-035#AT-SPEC-MANDATORY>

> [p34] **Read once.** An `@spec` target is read only on **first** encounter, never re-read blindly — see the read-set (§below).
>
> <spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-035#READ-ONCE>

> [p35] **Bare `spec://…`** (no `@`) is at the agent's discretion — read it if useful, skip it otherwise. (A future algorithmic agent narrows this further.)
>
> <spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-035#BARE-SPEC-DISCRETIONARY>

## Адрес никогда не двигается {#immutable}

[p36] После публикации якорь неизменяем. Переименование раздела или правила оставляет на старом якоре надгробие, указывающее на новый, так что ссылка, написанная год назад, всё ещё попадает куда надо. Перемещение файлов адреса тоже не меняет: адрес логический, путь — физика.

> [p37] **L1 — physics moves, addresses do not.** `spec://`
> addresses, anchors, fact ids, specmap edges, `scope!` citations and
> recorded verdicts are LOGICAL and do not change: only the physical
> prefix maps differently (`vibevm/vibespecs/common/PROP-000.xml`
> still canonicalises to the same `common/PROP-000` document path).
> Any relayout step that would rename an address is a defect of the
> step.
>
> <spec://org.vibevm.core/vibevm/common/PROP-052#ADDRESSES-SURVIVE-THE-MOVE>

> [p38] **Anchors are immutable; a rename is a tombstone** — for published documentation pages as for specs, and for translations, which must match their source.
>
> <spec://org.vibevm.core/vibevm/common/PROP-057#INV-ANCHORS-IMMUTABLE>

## Особые случаи и правила {#edge-cases}

[p39] Сгенерированный файл никогда не цель цитаты: цитируйте исходный документ, а не скомпилированную [стартовую полосу](../glossary/index.xml#boot-lane).

> [p40] **A generated `STATIC.md` is not a citation target** — authored text never cites `spec://…/boot/STATIC#…`; the lane is compiler output, and source-of-truth is the package source under `vibedeps/` (PROP-035 §11's lint, B-011 §6.1).
>
> <spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-009#COMPILED-LANE-IS-NOT-A-CITATION-TARGET>

[p41] `vibe facts check` проверяет разметку спецификаций пакета; `vibe check` проверяет пакет целиком; обе запускаются перед публикацией.

[p42] Код тоже может цитировать спецификации, атрибутом на элементе, который воплощает правило, и тогда карта отвечает, какой код стоит за каким правилом, в обе стороны; эту карту объясняют страницы об архитектуре этого руководства.

