# Продвинутые Markdown и XML {#root}

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

[p01] Эта страница берёт пакет, который вы пишете в Markdown, и показывает XML, который вместо него читает ваш агент. По дороге вы узнаете, зачем VibeVM превращает каждый текст в XML и почему автору это ничего не стоит. Узнаете, как назвать правило так, чтобы машина могла на него указать. И научитесь делить длинный текст на короткий заголовочный файл и длинное тело — так программисты на C и C++ делят библиотеку. Вы соберёте один маленький пакет руками, посмотрите, как он компилируется, и докажете, что две формы — одно и то же. Заложите около получаса; агент не понадобится.

## Что понадобится {#what-you-need}

[p02]
| Что | Зачем | Где взять |
| --- | --- | --- |
| `vibe` | создаёт проект, конвертирует текст и компилирует его | [Установить vibe](../start/install-vibe.xml) |
| текстовый редактор | три коротких файла вы пишете руками | любой |
| около получаса | весь путь, без агента |  |

## Почему всё становится XML {#why-xml}

[p03] В проекте, который об этом просит, каждый текст, принесённый пакетом, ложится на диск как XML, в какой бы форме его ни написал автор. Эти тексты — [спецификации](../glossary/index.xml#specification): правила, которые пакет задаёт агентам, работающим под ним; а причина конверсии — читатель. Модель, которая читает `<TESTS-FIRST fact="true" status="spec/done">`, знает, где правило начинается, где кончается, как называется и в каком оно состоянии, не угадывая по вёрстке. Проза заставляет модель выводить все четыре ответа самой.

[p04] Измерения согласны с интуицией. В эталонном исследовании Юань Суй с коллегами дали GPT-3.5 и GPT-4 одни и те же таблицы, записанные шестью способами: от простого текста с разделителями до CSV, JSON, XML, HTML и Markdown ([WSDM 2024](https://arxiv.org/abs/2305.13062)). На семи видах вопросов разметка, называющая свои части, обошла тот же текст в прозе. Лучше всех оказался HTML — на 6,76 процента по цифре самой статьи. В одной задаче, где нужно сказать, где начинаются и кончаются части таблицы, XML набрал 96,00 процента против 93,00 у простого текста и 92,33 у Markdown. Исследование про таблицы, и ровно до этого места страница его и доводит.

[p05] Совет производителя говорит то же с другой стороны. Руководство Anthropic по промптам для Claude — рекомендация, а не исследование — советует оборачивать каждый вид содержимого в свой XML-тег, чтобы модель разбирала длинный промпт без двусмысленности ([руководство](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices)). Олег Чирухин, автор VibeVM, обнаружил это на собственной практике раньше, чем об этом сказала хоть одна опубликованная работа. Цель `xml` — форма, в которой живут проекты этого руководства, и та, которую спецификация называет будущей основной.

> [p06] **`xml` target:** every source — including Markdown —
> emits as dialect XML through the pivot. Mixed input translating into
> CLEAN XML (never mixed output) is this target's acceptance bar, because
> it is the future primary.
>
> <spec://org.vibevm.core/vibevm/common/PROP-045#TARGET-XML>

[p07] Имена значат не меньше скобок. Раздел становится элементом, названным по себе самому, `<tests-first title="Tests before fixes">`. Правило становится элементом, названным по своему идентификатору, с одним опознавательным атрибутом, `fact="true"`, так что читатель, ничего не знающий о вашем словаре, находит каждое правило одной проверкой. Первый читатель этого диалекта — агент, а тегу, который говорит, что в нём лежит, легенда не нужна.

> [p08] **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>

> [p09] **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>

[p10] Одно предостережение для тех, кто обучает модели. Свидетельства выше — про текст, который модель читает. Для текста, который модель пишет, всё наоборот: принуждение ответа к JSON, XML или YAML может стоить качества рассуждений ([Тэм с коллегами, EMNLP 2024](https://arxiv.org/abs/2408.02442)). VibeVM задаёт форму тому, что агент читает, и никогда — тому, что он отвечает, так что этот результат его не касается.

## Markdown и XML: одна модель под капотом {#one-model}

[p11] Под капотом две формы — одна сущность. Каждый документ, Markdown или XML, разбирается в одно дерево: заголовок, статус и разделы, вложенные по глубине заголовков. Внутри разделов лежат абзацы, списки, таблицы, блоки кода и цитаты, и некоторые из них несут [факт](../glossary/index.xml#fact) — одно именованное правило со статусом. vibe никогда не переписывает текст Markdown в текст XML. Он разбирает в дерево и печатает из него, в любую сторону.

> [p12] **Decision (ADR-part).** There is ONE internal document
> model — the pivot — and every format is a frontend (parse into it) or a
> backend (emit from it). The pivot is the progress-markup semantic tree the
> tree already owns: document → nested sections (the heading hierarchy with
> anchors) → blocks (paragraph / list / table / fence / quote) → facts
> (anchored units with status and body spans), plus the `<status>` document
> element and fragment wrappers. Conversion between formats is always
> parse → pivot → emit; there is no direct MD↔XML text rewriting.
> Alternatives weighed: per-pair converters (N² growth, drift between pairs)
> and a lossless-CST pivot preserving all whitespace (cost without a
> consumer; the degradation law below makes semantic-level fidelity the
> contract). **Resolved by the XML-MEASURE map (2026-08-21): there is no
> single Markdown frontend to widen — FOUR independent families read
> different MD subsets today (progress-core's scanner, the vendored
> specmap engine's mdspec, the boot/tree directive readers, vibe-check's
> point scanners), with real dialect drift already between them (fence
> grammar run-matching vs prefix-toggling). The pivot is therefore a NEW
> shared crate — `vibe-specdoc` — owning the document IR and both
> frontends/backends; host consumers converge on it. The vendored specmap
> engine is engine-workspace territory (sync-engines law): its XML
> frontend is built in the AUTHORED engine workspace and写-throughs as its
> own slice (S4b), never patched in the vendored copy.*
>
> <spec://org.vibevm.core/vibevm/common/PROP-045#PIVOT-MODEL>

[p13] Авторы компиляторов называют такое дерево *промежуточным представлением*, IR: единственная форма, в которую разбирает каждый фронтенд и из которой печатает каждый бэкенд. Компилятору для трёх языков и четырёх процессоров нужны три фронтенда и четыре бэкенда, а не двенадцать переводчиков, и правило, один раз сформулированное о дереве, действует для всех языков. У VibeVM та же форма: два фронтенда и два бэкенда, Markdown и XML с каждой стороны.

> [p14] Everything downstream operates on a single **document IR**: a DOM-like tree. Markdown and (future) XML are two frontends parsed into the same tree, so algorithms written against the IR scale to deeply nested XML for free.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-035#DOCUMENT-IR>

[p15] Дерево нужно ему по тем же двум причинам. Без него каждой паре форм понадобился бы свой конвертер, а конвертеры расходятся. Обзор кода в 2026 году нашёл внутри vibe четыре читателя Markdown, каждый со своим чуть иным диалектом: болезнь, от которой одно общее дерево и должно лечить. И каждый инструмент, читающий спецификацию, читает XML через то же дерево: проверка фактов, компилятор, который собирает список чтения агента, маршрутизатор, который разрешает адрес. Поэтому документ в XML и его двойник в Markdown дают каждому инструменту один и тот же ответ.

> [p16] **Decision (ADR-part): scanners read XML through its
> canonical Markdown projection — one dispatch layer above the parser, no
> dependency cycle.** `vibe-specdoc` depends on `progress-core` (its MD
> frontend is the adapter), so progress-core cannot itself call specdoc.
> The consumers dispatch instead: a `.xml` spec entering any scanner
> (progress, check, specmap-host, show) is first projected
> `from_xml → to_markdown` — deterministic and canonical by S1's emitter —
> and the projection feeds the existing MD machinery; units, facts,
> anchors, hashes and verdict staleness all work unchanged, and a source
> edit moves the projection exactly when it moves meaning. Alternatives
> weighed: a native XML unit-walker in every scanner (a fifth and sixth
> parser family — the disease the measure named), and inverting the crate
> dependency (progress-core consuming specdoc — a cycle). RECORDED
> DEGRADATION, honest: a diagnostic for an XML source cites
> projection-relative line numbers, and v1 marks such diagnostics with
> the projection notice rather than pretending; native source positions
> are follow-up work riding the specmap-engine slice (S4b).
>
> <spec://org.vibevm.core/vibevm/common/PROP-045#PROJECTION-READ>

[p17] У дерева есть будущее за пределами конверсии. Компилятор называет его уровни — от текста одного документа до всего достижимого замыкания проекта, — и плагин компилятора может их читать и переписывать. Агент с длинным горизонтом когда-нибудь сможет планировать поверх этого замыкания: не стена текста, а граф именованных единиц с адресами. Это направление, а не возможность, которую можно запустить сегодня. И сам VibeVM — не агент. Он никогда не читает ваши правила, чтобы по ним действовать; он готовит текст, дерево и адреса для того агента, которого запускаете вы.

> [p18] The compiler's IR is **multi-level** (the MLIR shape, because compilation is progressive lowering): **`source`** — one document's raw text plus its spec address; **`document`** — one parsed tree; **`closure`** — the whole reachable compilation unit and graph; **`lane`** — the assembled ordered nodes and provenance; **`emitted`** — serialised bytes per artifact. R3 made these explicit typed carriers and R6.2 froze their epoch-1 wire.
>
> <spec://org.vibevm.core/vibevm/common/PROP-054#IR-LEVELS>

> [p19] **Decision (owner ruling 2026-08-25; implemented through R6.5-D).** A plugin may be a **full compiler extension** — a pass with complete access to the packages' IR, free to change it arbitrarily: new optimisations, new frontends, new backends, the LLVM posture verbatim. Users can build the compiler onward from here. The price of the power is explicitness: the pass tier exists only behind a dedicated manifest flag, only under host activation, only in-process (`builtin`/`native`), and always on the record (§7.4.3). D1 exposes exact selected native backends through `vibe extensions compile`; D2 commissions a genuine installed dependency whose first-class TXT frontend feeds its JSON backend. Evidence: `ee7f6f2d`, `def9909a`, `56307492`.
>
> <spec://org.vibevm.core/vibevm/common/PROP-054#PASS-TIER-LAW>

[p20] Вывод для вас прост. Пишите спецификации своих пакетов в Markdown — в форме, с которой уже справляются ваш редактор и ваши рецензенты. Каждое свойство формы XML придёт само: именованные элементы, факты, которые проверяет машина, адрес у каждого правила. Шаги ниже делают ровно это и в конце это доказывают.

## Шаг 1: проект, который материализуется в XML {#create-project}

[p21] 1. Создайте проект в пустой папке, как на странице [Создать первый проект](../start/first-project.xml). Имя станет именем папки:

[p22]
```sh
vibe init review-lab
```

```output
Initializing project `review-lab` in `review-lab`
  ✓ created  vibevm/vibespecs/boot/00-core.md
  ✓ created  vibevm/vibespecs/boot/90-user.md
  ✓ created  vibe.toml
  ✓ created  vibe.lock
  ✓ created  .vibe/.gitignore
  ✓ created  .gitignore
  ✓ created  vibevm/vibespecs/boot/INDEX.md
  ✓ created  CLAUDE.md
  ✓ created  AGENTS.md
  ✓ created  GEMINI.md

Done. Project `review-lab`: 10 files created, 0 kept.

Next:
  • edit vibevm/vibespecs/boot/00-core.md and vibevm/vibespecs/common as your project takes shape
  • install packages with `vibe install <kind>:<name>` (e.g. flow:wal)
```

[p23] 2. Откройте `review-lab/vibe.toml`, [манифест](../glossary/index.xml#manifest) проекта, и добавьте одну строку под `[project]`:

[p24]
```toml
[project]
spec_format = "xml"
name = "review-lab"
```

[p25] 3. Дальше работайте внутри папки: `cd review-lab`.

[p26] Эта строка решает, в какой форме ляжет каждый текст, который приносит пакет. С `xml` vibe конвертирует то, что автор написал в Markdown, пока копирует пакет в проект, а XML копирует как есть. Ваши собственные файлы не конвертируются никогда. Без этой строки каждый файл сохраняет форму, в которой его написал автор.

> [p27] **The setting and its home (revised by the measure —
> reproducibility rules).** The materialisation format is a REPRODUCIBLE
> project property, so its canonical home is the project manifest:
> `vibe.toml [project] spec_format = "mixed" | "markdown" | "xml"`, with
> the effective value recorded in the slot record defined by PROP-054 §9.2
> so two machines materialise identically. The user-config
> family supplies only the operator DEFAULT for projects that do not pin
> one (`[install] spec_format`, beside `slot_integrity`), per the standing
> precedence CLI > env > project > user > built-in; vibe-settings is
> barred from this key by PROP-040's own boundary (app prefs never extend
> vibe.toml). **`mixed` is the built-in default** — for an all-MD world
> byte-for-byte today's behaviour; the flip of the default to XML is the
> owner's word, never silent.
>
> <spec://org.vibevm.core/vibevm/common/PROP-045#SETTING>

> [p28] **`xml` target:** every source — including Markdown —
> emits as dialect XML through the pivot. Mixed input translating into
> CLEAN XML (never mixed output) is this target's acceptance bar, because
> it is the future primary.
>
> <spec://org.vibevm.core/vibevm/common/PROP-045#TARGET-XML>

## Шаг 2: создать заготовку пакета {#scaffold}

[p29] 1. Добавьте в проект пакет. Это `flow`, пакет рабочих правил для агента, и он просит формат `normal`, смысл которого объясняет шаг 4:

[p30]
```sh
vibe init package org.acme/review --kind flow --format normal
```

```output
Creating package `org.acme/review` in `<TMP>/work/review-lab`
  ✓ created  vibevm/vibepacks/org.acme/review/v0.1.0/vibe.toml
  ✓ created  vibevm/vibepacks/org.acme/review/v0.1.0/vibevm/vibespecs/boot/10-flow-review.md
  ✓ created  vibevm/vibepacks/org.acme/review/v0.1.0/README.md
  • kept     vibevm/vibespecs/boot/INDEX.md (regenerated)
  • kept     CLAUDE.md (regenerated)
  • kept     AGENTS.md (regenerated)
  • kept     GEMINI.md (regenerated)

Done. Project `org.acme/review`: 3 files created, 4 kept.

Next:
  • edit vibevm/vibespecs/boot/00-core.md and vibevm/vibespecs/common as your project takes shape
  • install packages with `vibe install <kind>:<name>` (e.g. flow:wal)
```

[p31] 2. Откройте манифест, который написала заготовка:

[p32]
```sh
cat vibevm/vibepacks/org.acme/review/v0.1.0/vibe.toml
```

```output
[package]
group = "org.acme"
name = "review"
kind = "flow"
version = "0.1.0"
epoch = 1
authors = ["vibevm docs fixtures"]
license = "UPL-1.0"
description = ""
format = "normal"

[boot_snippet]
source = "vibevm/vibespecs/boot/10-flow-review.md"
category = "flow"
link = "dynamic"
```

[p33] 3. Измените две строки: дайте пакету `description` и поставьте `link = "static"`. Конец файла станет таким:

[p34]
```toml
description = "The team's code review rules, as a contract with its reasons."
format = "normal"

[boot_snippet]
source = "vibevm/vibespecs/boot/10-flow-review.md"
category = "flow"
link = "static"
```

[p35] [Тип связи](../glossary/index.xml#link-type) (*link type*) говорит, как текст пакета доходит до агента. При `static` vibe компилирует текст в тот единственный файл, который агент читает первым, целиком, в начале каждой сессии. При `dynamic`, выборе заготовки, он перечисляет файл, чтобы агент открыл его сам. Эта страница берёт `static`, чтобы вы увидели компилятор за работой.

> [p36] `link = "static"` — the contribution's boot text is compiled into `STATIC.md` ahead of time (whole, anchor-qualified — §2.3). Read first, one read, maximum attention weight. The **emergency priority lane** — for top-level skills and critical disciplines whose priority must be guaranteed by position, not by trusting agent-side resolution. Used sparingly; it duplicates the text on disk.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-009#LINK-STATIC>

> [p37] `link = "dynamic"` — **the default.** `vibe` resolves the contribution to a concrete path in `INDEX.md`; the agent reads it dynamically, on demand. An optional `when` condition gates the read: with a `when` it is a **conditional** INCLUDE (loaded only when the condition holds) — mechanically the subskill `lazy-pull` delivery mode; without one it is read unconditionally. The `when` draws on the subskill `[activation]` probe vocabulary (PROP-003 §2.5) — one probe grammar across both mechanisms. **v1 implements the `os:` probe end-to-end** — `when = "os:windows"` matches the session's operating system (`windows` / `macos` / `linux`); the remaining probes are reserved until PROP-003's activation engine is built.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-009#LINK-DYNAMIC>

## Шаг 3: якоря и факты, в Markdown {#anchors-and-facts}

[p38] Пакету нужны два документа. Первый — заметка, которую агент читает в начале сессии, [стартовый фрагмент](../glossary/index.xml#boot-snippet) (*boot snippet*) пакета. Второй — контракт: сами правила, каждое со своим адресом.

[p39] 1. Замените заметку заготовки, `vibevm/vibepacks/org.acme/review/v0.1.0/vibevm/vibespecs/boot/10-flow-review.md`, на эту:

[p40]
```markdown
# Review flow

Before you open a change for review, hold it to the review rules. This
note pulls the rules in; each rule is one fact with an address, and the
reasons follow the rules. Cite a rule by its address, for example
`spec://org.acme/review/contract/REVIEW#ONE-IDEA`.

#use spec://org.acme/review/contract/REVIEW#root
```

[p41] Строка, начинающаяся с `#use`, — директива: инструкция компилятору, а не проза. Она втягивает контракт перед заметкой, так что агент встречает правила раньше заметки, которая на них ссылается. Файлы этой страницы написаны по-английски, как и вывод команд, чтобы примеры совпадали с тем, что вы увидите у себя.

[p42] 2. Создайте `vibevm/vibespecs/contract/REVIEW.md` в том же пакете:

[p43]
```markdown
# Review rules {#root}

<status stage="spec" state="done"/>

The rules a reviewer holds every change to. Each rule is one anchored
fact with a status.

## One idea per change {#one-idea}

@fact:ONE-IDEA A change under review carries one idea, named in its first line. @status:spec/done

## Tests before fixes {#tests-first}

@fact:TESTS-FIRST A change that alters behaviour carries a test that fails without it. @status:spec/done
```

[p44] Прочитайте файл так, как читает машина. Заголовок несёт [якорь](../glossary/index.xml#anchor) в фигурных скобках, `{#one-idea}`: имя, которым пользуется ссылка, часть адреса после `#`. Абзац, который открывается `@fact:ONE-IDEA`, — факт, одно заякоренное правило со статусом. `@status:spec/done` в его конце говорит, что правило решено и ещё не построено. Элемент `<status>` под заголовком — тот же маркер для всего документа. Якоря заголовков и идентификаторы фактов делят одно пространство имён, поэтому ни один не повторяется в документе дважды.

> [p45] **Fact anchors — the anchored-when-marked law** *(owner, 2026-07-24;
>      spelling amended 2026-08-06)*. A stable fact address is written
>      `@fact:<ID>` as the **first token** of a paragraph or list item. The
>      **legacy** spelling `##<ID>` means the same and is still read.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-facts/PROP-043#FACT-ANCHOR-SYNTAX>

> [p46] **Every unit that carries a status marker — paragraph or list item —
>      MUST also carry a `@fact:<ID>` anchor**; a marked, anchor-less unit is a
>      `check` error.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-facts/PROP-043#ANCHORED-WHEN-MARKED>

> [p47] `@status:<stage>/<state>` and `@status:<stage>` are macro-equivalents of a
> point marker. The **legacy** spellings `@<stage>/<state>` and `@<stage>` mean
> exactly the same and are still read, so a document written before the
> qualified form keeps parsing.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-facts/PROP-043#SHORTHAND-FORMS>

> [p48] `<ID>` is
>      `[A-Za-z][A-Za-z0-9_-]*`; the unit is then addressable as
>      `spec://…/<doc>#<ID>`, sharing one address space with the heading
>      `{#anchor}`s — a duplicate across both forms is a `check` error. The
>      **address is unchanged by the spelling**: it names the id, never the
>      opener.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-facts/PROP-043#FACT-ID-GRAMMAR>

[p49] Регистр идентификатора — сигнал. Идентификаторы в верхнем регистре помечают правила с обязывающей силой; в нижнем — заголовки, вводные и заметки. Адрес первого правила — `spec://org.acme/review/contract/REVIEW#ONE-IDEA`. Он складывается из [координаты](../glossary/index.xml#coordinate) пакета, пути документа под `vibevm/vibespecs/` без расширения и якоря. Адрес не меняется, когда файл меняет форму.

> [p50] **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>

> [p51] `spec://` addressing is format-blind: anchors
> are `id` attributes in XML and `{#…}`/first-token anchors in MD, minted
> into the same address space; a document's address does not change when
> its serialisation does.
>
> <spec://org.vibevm.core/vibevm/common/PROP-045#ADDRESSING-UNCHANGED>

[p52] 3. Проверьте разметку пакета:

[p53]
```sh
vibe facts check --path vibevm/vibepacks/org.acme/review/v0.1.0
```

```output
progress check: clean (2 files, 0 warning(s))
```

[p54] Проверка читает каждый документ под `vibevm/vibespecs/` пакета. Маркер на абзаце без якоря и идентификатор, определённый дважды, — ошибки, которые называют строку.

[p55] 4. Установите пакет в его собственный проект:

[p56]
```sh
vibe install org.acme/review --assume-yes
```

```output
Resolving 1 root package…

Materialising 1 package into vibedeps/:
  org.acme/review@0.1.0

closure diff:
  → + org.acme/review@0.1.0 (root-edge)
  → lane vibevm/vibespecs/boot/INDEX.md: 737 -> 781 B
  → lane vibevm/vibespecs/boot/STATIC.xml: absent -> 2809 B

Materialised 1 package into vibedeps/; regenerated boot artifacts for 1 node(s).
```

[p57] Установка копирует пакет в `vibevm/vibedeps/`, конвертируя оба документа в XML. Она же компилирует [стартовую полосу](../glossary/index.xml#boot-lane) (*boot lane*) — упорядоченный список файлов, которые агент читает в начале сессии. Последние две строки диффа — это она: в `INDEX.md` появилась строка с именем скомпилированного файла, а `STATIC.xml` возник.

[p58] 5. Откройте контракт таким, каким его прочитает агент:

[p59]
```sh
cat vibevm/vibedeps/org.acme.review/0.1.0/vibevm/vibespecs/contract/REVIEW.xml
```

```output
<?xml version="1.0" encoding="UTF-8"?>
<spec xmlns="https://vibevm.org/spec/1">
  <title id="root">Review rules</title>
  <status stage="spec" state="done"/>
  <p>The rules a reviewer holds every change to. Each rule is one anchored
fact with a status.</p>
  <one-idea title="One idea per change">
    <p><ONE-IDEA fact="true" status="spec/done">A change under review carries one idea, named in its first line.</ONE-IDEA></p>
  </one-idea>
  <tests-first title="Tests before fixes">
    <p><TESTS-FIRST fact="true" status="spec/done">A change that alters behaviour carries a test that fails without it.</TESTS-FIRST></p>
  </tests-first>
</spec>
```

[p60] Каждая часть названа по себе самой. Раздел `{#one-idea}` стал элементом `<one-idea>` с заголовком в атрибуте. Факт стал `<ONE-IDEA fact="true" status="spec/done">`. Префикс `@fact:` и суффикс `@status:` из текста исчезли: они были написанием, а дерево хранит только смысл. Строчная разметка Markdown — обратные кавычки, ссылки — едет внутри текста без изменений.

> [p61] **Decision (ADR-part).** Inline content —
> emphasis, inline code, links, `##NAME` citations, `spec://` addresses —
> rides INSIDE text nodes as literal Markdown conventions, in both
> directions. The pivot does not model inline grammar. Why: the markup
> contract already treats inline code as opaque; round-tripping stays
> byte-stable at the text level; XML authorship needs no inline vocabulary;
> and every consumer that reads fact bodies today keeps reading the same
> strings. Alternative weighed — a full inline element vocabulary
> (`<code>`, `<a>`, `<b>`) — rejected as cost without a consumer and a
> fresh drift surface between two inline grammars.
>
> <spec://org.vibevm.core/vibevm/common/PROP-045#INLINE-STAYS-MARKDOWN>

## Заголовочный файл и его реализация {#headers}

[p62] Контракт, который говорит всё, обходится дорого. Каждое слово стартовой полосы читает каждый агент в начале каждой сессии. Поэтому текст, который должен быть перед глазами всегда, хочет быть коротким, а рассуждение за ним хочет лежать там, куда агент дотянется, когда спросит. C решил задачу такой формы в 1970-е двумя файлами, и C++ сохранил это решение. Заголовочный файл, `.h`, в нескольких строках объявляет, что предлагает библиотека; единица трансляции, `.c` или `.cpp`, несёт реализацию. Все, кто пользуется библиотекой, включают заголовок, и никто не вставляет реализацию в собственный код.

> [p63] Inspired by C/C++ `.h` / `.cpp`:
>
> <spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-035#hpp-cpp-inspiration>

[p64] VibeVM заимствует это разделение для текста. Пакет в формате `normal` держит под `vibevm/vibespecs/` две папки. `contract/` — заголовок: маленький, дешёвый в загрузке, поверхность, которую видят другие пакеты и агенты. `source/` — реализация: тяжёлое тело, которое втягивается только тогда, когда кто-то попросит. Компилятор C видит всю программу и сам сводит объявления с определениями; у vibe такого взгляда на ваш текст нет, поэтому контракт сам называет свою реализацию директивой `#source`. У формата по умолчанию, `simple`, ничего этого нет: такой пакет несут целиком и читают потому, что он есть.

> [p65] **`contract/`** — small, simple, boot-snippet-like. The surface a package exposes outward; short files, cheap to load. The analogue of a header.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-035#DIR-CONTRACT>

> [p66] **`source/`** — large, heavy. The full implementation; pulled only when actually needed. The analogue of a translation unit.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-035#DIR-SOURCE>

> [p67] Because the structural executor lacks a C++ compiler's global view of the source tree, we use a deliberate hack: **the contract author declares what implements it, via `#source`** (§7.3). The author hand-draws edges a globally-aware compiler would infer.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-035#SOURCE-HACK>

> [p68] **`format = "simple"`** — **the default** (absent `format`, a package is `simple`). Legacy / adapted prompts, carried **whole**, with no VibeVM-specific structure — for importing existing corpora without rewriting them, and the fail-safe posture. Rules: inclusion in `[requires.packages]` means (a) structural — the agent reads the file; (b) static — its text is compiled into the target. If `[boot_snippet].source` names a file, only that file is read/spliced; **absent even that, every file in the package is read/spliced by a recursive walk** — the over-load is the author's problem, the deliberate cost of not adopting `normal`.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-035#FORMAT-SIMPLE>

> [p69] **`format = "normal"`** — the VibeVM-native form, **opt-in**: the `contract` / `source` split (§4), directives (§7), and the compiler (§8). A `normal` package is **not read just because it is present** — it participates only when something actually `#use`s it (§7.2). This is tree-shaking; the optimized posture for authors who understand the machinery, at the price of structuring the package correctly.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-035#FORMAT-NORMAL>

[p70] Спецификации называют этот механизм наследованием, как в C++: один документ строится на другом, не копируя его текст. Втягивают две директивы, и с одной вы уже знакомы. `#use` называет документ или раздел, который нужно прочитать раньше текста, который им пользуется; компилятор копирует его вперёд, и копия приводит с собой всё, что втягивает сам скопированный документ. `#source` называет реализацию контракта, и компилятор компилирует её вслед за контрактом. В скомпилированном файле обеих директив уже нет — они израсходованы. Третья, `#embed`, вклеивает ровно один адресованный узел на то место, где стоит: макрос, а не include.

> [p71] **Inline mode.** The same, statically: the `#use`d library's text is **fully copied higher up in `STATIC.md`** so it is available before the user.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-035#USE-INLINE>

> [p72] Like a C++ interface, but with section-level merging. `contract` sections are the exposed surface; `#source` names the file(s) that implement them. Sections are treated as the analogue of class methods, needing a merge (in the static build) or virtual-lookup (in structural mode) mechanism.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-035#SOURCE-DEF>

> [p73] **`#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>

## Шаг 4: отделить контракт от его причин {#split}

[p74] 1. Создайте в пакете `vibevm/vibespecs/source/details.md`, реализацию контракта:

[p75]
```markdown
# Review rules, the reasons {#details}

## Why one idea per change {#one-idea-why}

@fact:ONE-IDEA-WHY A change with two ideas cannot be reverted one idea at a time, and its review takes twice as long. @status:spec/done

## Why tests before fixes {#tests-first-why}

@fact:TESTS-FIRST-WHY A fix without a failing test proves nothing: the test states what was wrong. @status:spec/done

## What a reviewer checks {#checklist}

- @fact:CHECK-SCOPE The first line names the one idea, and every hunk serves it. @status:spec/done
- @fact:CHECK-TEST The test fails on the parent commit and passes on this one. @status:spec/done
```

[p76] Каждый якорь здесь отличается от якорей контракта: `one-idea-why` рядом с `one-idea`; особые случаи ниже объясняют почему. Последний раздел — список, в котором каждый пункт — факт.

[p77] 2. В `contract/REVIEW.md` добавьте одну строку после первого абзаца:

[p78]
```markdown
#source spec://org.acme/review/source/details
```

[p79] Адрес называет целый документ, без якоря, так что компилятор берёт его от заголовка и ниже.

[p80] 3. Установите снова:

[p81]
```sh
vibe install org.acme/review --assume-yes
```

```output
Resolving 1 root package…

Materialising 1 package into vibedeps/:
  org.acme/review@0.1.0

closure diff:
  → lane vibevm/vibespecs/boot/STATIC.xml: 2809 -> 4557 B

Materialised 1 package into vibedeps/; regenerated boot artifacts for 1 node(s).
```

[p82] Ни один пакет не добавлен, поэтому полоса — единственная строка диффа: скомпилированный файл вырос на реализацию.

[p83] 4. Спросите vibe, что агент читает первым:

[p84]
```sh
vibe tree --plain
```

```output
project: <TMP>/work/review-lab
STATIC.xml: 4557 bytes, 69 lines, 1 contribution(s)
packages: 1   roots: 1
columns: load  T=transitive  C=condition  S=in STATIC.xml

org.acme/review  static   .  .  x
```

[p85] Один пакет, связанный как `static`, и `x` говорит, что его текст скомпилирован в `STATIC.xml`.

[p86] 5. Откройте скомпилированный файл:

[p87]
```sh
cat vibevm/vibespecs/boot/STATIC.xml
```

```output
<!-- vibe:c1 vibevm/vibespecs/boot/STATIC.xml — generated by vibe, do not edit. -->
<!-- vibe:c1 The static boot lane (PROP-009 §2.3): the highest-priority -->
<!-- vibe:c1 contributions, compiled anchor-qualified. Read this first, in full. -->

<!-- vibe:c1 RESOLUTION RULES — read these five lines before anything else:
  1. Labels in this file are qualified: <origin-slug>-%2D<original>. The origin
     is named by the provenance comment above each block; the original short
     label is the tail after the last `-%2D`.
  2. A short label you cannot find here → check the RENAMED ANCHORS table
     below for its qualified heirs; never guess among look-alikes.
  3. Full spec:// addresses resolve against package SOURCES under vibevm/vibedeps/,
     never against this generated file. This file is a cache, not a target.
  4. `#use spec://… as X` binds a file-local alias; `@!X` is a mandatory read
     of X's target (same rules as @spec://…). In this compiled file every @!X
     is already rewritten to its full address.
  5. An ambiguous or unresolvable short reference is an ERROR to surface with
     candidates — never silently pick one. -->

<!-- vibe:c1 RENAMED ANCHORS (short → qualified heirs):
  CHECK-SCOPE → org-acme-%2Dreview-%2DCHECK-SCOPE (org.acme/review)
  CHECK-TEST → org-acme-%2Dreview-%2DCHECK-TEST (org.acme/review)
  ONE-IDEA → org-acme-%2Dreview-%2DONE-IDEA (org.acme/review)
  ONE-IDEA-WHY → org-acme-%2Dreview-%2DONE-IDEA-WHY (org.acme/review)
  TESTS-FIRST → org-acme-%2Dreview-%2DTESTS-FIRST (org.acme/review)
  TESTS-FIRST-WHY → org-acme-%2Dreview-%2DTESTS-FIRST-WHY (org.acme/review)
  checklist → org-acme-%2Dreview-%2Dchecklist (org.acme/review)
  details → org-acme-%2Dreview-%2Ddetails (org.acme/review)
  one-idea → org-acme-%2Dreview-%2Done-idea (org.acme/review)
  one-idea-why → org-acme-%2Dreview-%2Done-idea-why (org.acme/review)
  root → org-acme-%2Dreview-%2Droot (org.acme/review)
  tests-first → org-acme-%2Dreview-%2Dtests-first (org.acme/review)
  tests-first-why → org-acme-%2Dreview-%2Dtests-first-why (org.acme/review) -->

<!-- vibe:c1 vibe:static org.acme/review — vibevm/vibedeps/org.acme.review/0.1.0/vibevm/vibespecs/boot/10-flow-review.xml -->

<?xml version="1.0" encoding="UTF-8"?>
<spec xmlns="https://vibevm.org/spec/1">
  <title id="org-acme--review--root">Review rules</title>
  <status stage="spec" state="done"/>
  <p>The rules a reviewer holds every change to. Each rule is one anchored
fact with a status.</p>
  <org-acme--review--one-idea title="One idea per change">
    <p><org-acme--review--ONE-IDEA fact="true" status="spec/done">A change under review carries one idea, named in its first line.</org-acme--review--ONE-IDEA></p>
  </org-acme--review--one-idea>
  <org-acme--review--tests-first title="Tests before fixes">
    <p><org-acme--review--TESTS-FIRST fact="true" status="spec/done">A change that alters behaviour carries a test that fails without it.</org-acme--review--TESTS-FIRST></p>
  </org-acme--review--tests-first>
  <org-acme--review--details title="Review rules, the reasons">
    <org-acme--review--one-idea-why title="Why one idea per change">
      <p><org-acme--review--ONE-IDEA-WHY fact="true" status="spec/done">A change with two ideas cannot be reverted one idea at a time, and its review takes twice as long.</org-acme--review--ONE-IDEA-WHY></p>
    </org-acme--review--one-idea-why>
    <org-acme--review--tests-first-why title="Why tests before fixes">
      <p><org-acme--review--TESTS-FIRST-WHY fact="true" status="spec/done">A fix without a failing test proves nothing: the test states what was wrong.</org-acme--review--TESTS-FIRST-WHY></p>
    </org-acme--review--tests-first-why>
    <org-acme--review--checklist title="What a reviewer checks">
      <facts ordered="false">
        <org-acme--review--CHECK-SCOPE fact="true" status="spec/done">The first line names the one idea, and every hunk serves it.</org-acme--review--CHECK-SCOPE>
        <org-acme--review--CHECK-TEST fact="true" status="spec/done">The test fails on the parent commit and passes on this one.</org-acme--review--CHECK-TEST>
      </facts>
    </org-acme--review--checklist>
  </org-acme--review--details>
  <section title="Review flow">
    <p>Before you open a change for review, hold it to the review rules. This
note pulls the rules in; each rule is one fact with an address, and the
reasons follow the rules. Cite a rule by its address, for example
`spec://org.acme/review/contract/REVIEW#ONE-IDEA`.</p>
  </section>
</spec>
```

[p88] Читайте сверху. Сначала комментарии: правила, по которым агент трактует метки в этом файле, затем таблица всех переименованных якорей. XML запрещает два дефиса подряд внутри комментария, поэтому `--` в метке там записано как `-%2D`; элементы ниже несут настоящие `--`. Затем один документ XML: сначала контракт, за ним реализация, вкомпилированная одним вложенным разделом. Последней идёт заметка из фрагмента — простой `<section>`, потому что у её заголовка не было якоря. Строк `#source` и `#use` больше нет.

> [p89] **In source, absent in contract** — always counted; structural: readable at will; static: compiled in whole. *(Calling a section that exists only in the implementation is poor taste, but permitted — we deliberately impose no `private`/`public` access control.)*
>
> <spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-035#MERGE-SOURCE-ONLY>

[p90] Каждый якорь теперь несёт префикс `org-acme--review--` — метку своего происхождения. Два пакета, у которых раздел назван `root`, не столкнутся в одном файле, а таблица наверху говорит, чем стало каждое короткое имя. Ссылайтесь на исходный документ и никогда на этот файл: это кэш, который меняется всякий раз, когда меняется пакет.

> [p91] **Compiled labels are origin-qualified (B-011).** A compiled block's heading anchors and fact ids carry the §8 qualify phase's `<origin-slug>--` prefix, so the compiled document's label namespace is collision-free by construction; reversibility survives because the block's own marker key names the origin, and stripping that block's prefix restores the source labels.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-035#COMPILED-LABELS-ARE-QUALIFIED>

> [p92] **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>

[p93] 6. Откройте запись, которую vibe хранит рядом со своей копией пакета:

[p94]
```sh
cat vibevm/vibedeps/org.acme.review/0.1.0/.vibe-slot.toml
```

```output
schema = 1
source_hash = "sha256:30fef78663fe343057c2d36d65514d3708d2f5053a2d41b713a749a0b28cc8eb"
spec_format = "xml"
converter_recipe = "specdoc/4"
derived_hash = "sha256:e420b6841c71fab586dfd72fe4720881b77d056e8f7a86217d1174b5fde0feba"

[[file]]
path = "README.xml"
sha256 = "c26e751356947bca54346b4099dc297dcdcac44831087d06436b1cb5f51ecec1"
disposition = "converted"
source = "README.md"

[[file]]
path = "vibe.toml"
sha256 = "42cb5b83bdc0e8ef61bdbb46095688593b05e4ad07bc61b8b346c13be0e753c6"
disposition = "copied"
source = "vibe.toml"

[[file]]
path = "vibevm/vibespecs/boot/10-flow-review.xml"
sha256 = "925cd961b99debb27cd179a42908bdf006c5103422c2a216ee8fc874d30865d3"
disposition = "converted"
source = "vibevm/vibespecs/boot/10-flow-review.md"

[[file]]
path = "vibevm/vibespecs/contract/REVIEW.xml"
sha256 = "ebcda3b670be21b409aa07b2600582ceb57cdc7eef8dbb3ef0dfe92dbc4f7a11"
disposition = "converted"
source = "vibevm/vibespecs/contract/REVIEW.md"

[[file]]
path = "vibevm/vibespecs/source/details.xml"
sha256 = "d35c2052468004ff1fb0dfc7724a496d8773aa280e686ce597fd14615acf6a2d"
disposition = "converted"
source = "vibevm/vibespecs/source/details.md"
```

[p95] `spec_format` называет цель, `converter_recipe` — версию конвертера. Каждая строка говорит, был файл скопирован (`copied`) или сконвертирован (`converted`), и несёт хэш того, что легло на диск. Держите этот файл в уме: шаг 5 прочитает его снова.

> [p96] **The hash law under transformation.** Source identity is unchanged: lockfile `content_hash` and the machine store hash the source form. A transformed slot is a derived artifact whose identity and owned footprint are recorded in the single `.vibe-slot.toml`: `source_hash`, `spec_format`, versioned `converter_recipe`, optional `overlay_hash`, `derived_hash`, and per-file source/output/disposition/SHA-256 rows. The legacy `.vibe-derived.toml` is no longer written; its schema-1 reader remains only as a read-only compatibility tombstone until a real rematerialisation migrates the slot. Mixed slots verify their recorded source identity and payload; transformed slots additionally verify recipe, representation and `derived_hash`. A missing legacy record triggers one final migration, a malformed new record refuses, and mismatched valid state rematerialises through the owned diff; changing `spec_format` can never earn a presence skip. Semantic equivalence remains the converter's proof through the shared IR, never the hash's job.
>
> <spec://org.vibevm.core/vibevm/common/PROP-045#HASH-LAW>

## Эквивалентные формы {#equivalent-forms}

[p97] Конструкции, которыми пользовалась эта страница, бок о бок, из её собственных файлов:

[p98]
| Markdown | XML |
| --- | --- |
| `# Review rules {#root}` | `<title id="root">Review rules</title>` |
| `<status stage="spec" state="done"/>` | тот же элемент, без изменений |
| `## One idea per change {#one-idea}` и текст под ним | `<one-idea title="One idea per change">…</one-idea>` |
| абзац | `<p>…</p>` |
| `@fact:ONE-IDEA … @status:spec/done` | `<p><ONE-IDEA fact="true" status="spec/done">…</ONE-IDEA></p>` |
| `- @fact:CHECK-SCOPE … @status:spec/done`, список фактов | `<facts ordered="false"><CHECK-SCOPE fact="true" status="spec/done">…</CHECK-SCOPE></facts>` |
| `#source spec://…`, директива | `<p>#source spec://…</p>`, простой абзац |
| `# Review flow`, заголовок без якоря | `<title>Review flow</title>` |
| обратные кавычки, выделение, ссылки, адреса `spec://` | те же символы внутри текста |

[p99] Диалект закрыт и мал нарочно. Он выражает ровно то, что выражает Markdown, поэтому конверсия в любую сторону не теряет смысла, а элемент вне диалекта — громкая ошибка, а не молчаливый пропуск. У двух якорей нет собственного элемента: у того, что начинается с цифры, и у того, что совпадает со словом самого диалекта, таким как `title` или `list`. Для них встают общие `<section id="…">` и `<fact id="…">`, и каждый читатель принимает оба написания. Единственное исключение из правила эквивалентности — словарь пакетов документации, к которым принадлежит и это руководство: у их выполняемых примеров и живых цитат нет формы в Markdown, и в Markdown они проецируются только в одну сторону.

> [p100] **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>

> [p101] **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>

> [p102] **Decision (ADR-part; the recorded reopening of `##XML-DIALECT-IS-THE-MD-SUBSET`, 2026-09-11).**
> The decision-records law lets a decision be reopened only by a named trigger,
> and the trigger is named: a consumer appeared that needs constructs Markdown
> cannot express — the documentation genre of
> [PROP-057](PROP-057-documentation-packages-and-site.xml): a verifiable
> example with its expected output, a live citation of a specification rule, a
> generated reference block, an agent prompt with asserts. The dialect gains a
> **second, additive vocabulary** for that genre. The spec vocabulary keeps the
> MD-subset law unchanged; the documentation vocabulary is open only inside
> packages of kind `doc` and projects to Markdown one way, by law, not by
> defect.
>
> <spec://org.vibevm.core/vibevm/common/PROP-045#DOC-VOCAB-REOPENING>

## Шаг 5: сконвертировать и увидеть, что ничего не изменилось {#round-trip}

[p103] Вы написали Markdown и отгрузили XML. Последний шаг показывает, что XML, который вы написали бы руками, — тот же файл, байт в байт.

[p104] 1. Спросите конвертер, что потеряет конверсия:

[p105]
```sh
vibe refactor convert-source --to xml --dry-run vibevm/vibepacks/org.acme/review/v0.1.0/vibevm/vibespecs
```

```output
dry-run ir-stable-loss vibevm/vibepacks/org.acme/review/v0.1.0/vibevm/vibespecs/boot/10-flow-review.md
--- source
+++ reverse-projection
@@ -8,2 +8,3 @@
 #use spec://org.acme/review/contract/REVIEW#root

+

dry-run ir-stable-loss vibevm/vibepacks/org.acme/review/v0.1.0/vibevm/vibespecs/contract/REVIEW.md
--- source
+++ reverse-projection
@@ -16,2 +16,3 @@
 @fact:TESTS-FIRST A change that alters behaviour carries a test that fails without it. @status:spec/done

+

dry-run ir-stable-loss vibevm/vibepacks/org.acme/review/v0.1.0/vibevm/vibespecs/source/details.md
--- source
+++ reverse-projection
@@ -14,2 +14,3 @@
 - @fact:CHECK-TEST The test fails on the parent commit and passes on this one. @status:spec/done

+

summary converted=0 already=0 lossy-confirmed=0 refused=0 skipped-generated=0 skipped-foreign=0 skipped-harness=0 dry-run=3
```

[p106] Конвертер разбирает каждый файл в дерево, печатает XML, читает его обратно и снова печатает Markdown, а затем сравнивает. Вердикт `ir-stable-loss` значит, что дерево уцелело, а байты нет, и дифф показывает, что изменилось. Здесь это одна пустая строка в конце каждого файла, которую добавляет принтер Markdown. Без `--force` команда отказывает файлу из-за такой потери. В изменении смысла она отказывает всегда, потому что это был бы дефект конвертера, а не вашего файла.

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

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

[p109] 2. Сконвертируйте три документа. Команда пишет каждый `.xml` рядом с его `.md` и удаляет `.md` тем же действием. Дерево никогда не держит документ в обеих формах:

[p110]
```shell
vibe refactor convert-source --to xml --force vibevm/vibepacks/org.acme/review/v0.1.0/vibevm/vibespecs
```

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

[p112] 3. В манифесте пакета направьте `source` на новый файл, `vibevm/vibespecs/boot/10-flow-review.xml`.

[p113] 4. Установите снова:

[p114]
```sh
vibe install org.acme/review --assume-yes
```

```output
Resolving 1 root package…

Materialising 1 package into vibedeps/:
  org.acme/review@0.1.0

  → closure unchanged (1 packages)

Materialised 1 package into vibedeps/; regenerated boot artifacts for 1 node(s).
```

[p115] 5. Откройте запись ещё раз:

[p116]
```sh
cat vibevm/vibedeps/org.acme.review/0.1.0/.vibe-slot.toml
```

```output
schema = 1
source_hash = "sha256:6c7b5d64df7efadb3ebb7ba950902e3e3e1cc1a0a9c0a06f464c55a9f01bd209"
spec_format = "xml"
converter_recipe = "specdoc/4"
derived_hash = "sha256:908b2213850eba6789f655e7cfac4722328659e1eb5ea5017b8eb71055a152dc"

[[file]]
path = "README.xml"
sha256 = "c26e751356947bca54346b4099dc297dcdcac44831087d06436b1cb5f51ecec1"
disposition = "converted"
source = "README.md"

[[file]]
path = "vibe.toml"
sha256 = "018fe59c3ed0b41a24ad236798f5b8198c6ad983a58a9c72d7f73897a4d56e62"
disposition = "copied"
source = "vibe.toml"

[[file]]
path = "vibevm/vibespecs/boot/10-flow-review.xml"
sha256 = "925cd961b99debb27cd179a42908bdf006c5103422c2a216ee8fc874d30865d3"
disposition = "copied"
source = "vibevm/vibespecs/boot/10-flow-review.xml"

[[file]]
path = "vibevm/vibespecs/contract/REVIEW.xml"
sha256 = "ebcda3b670be21b409aa07b2600582ceb57cdc7eef8dbb3ef0dfe92dbc4f7a11"
disposition = "copied"
source = "vibevm/vibespecs/contract/REVIEW.xml"

[[file]]
path = "vibevm/vibespecs/source/details.xml"
sha256 = "d35c2052468004ff1fb0dfc7724a496d8773aa280e686ce597fd14615acf6a2d"
disposition = "copied"
source = "vibevm/vibespecs/source/details.xml"
```

[p117] Сравните её с записью шага 4. Каждый из трёх документов теперь `copied`, и хэш каждого — тот же, что был у него как у `converted`. То, что конвертер написал из вашего Markdown, — это то, что из него написала установка, байт в байт. Скомпилированный файл тоже не изменился:

[p118]
```sh
vibe tree --plain
```

```output
project: <TMP>/work/review-lab
STATIC.xml: 4557 bytes, 69 lines, 1 contribution(s)
packages: 1   roots: 1
columns: load  T=transitive  C=condition  S=in STATIC.xml

org.acme/review  static   .  .  x
```

[p119] Те же байты, те же строки. Вот что значит на практике одна модель под капотом. Форма, в которой вы пишете, — ваш выбор; текст, который доходит до агента, один и тот же. Проект с `spec_format = "markdown"` проходит ту же дорогу в обратную сторону. Markdown, который он пишет из вашего XML, — ваш исходный файл плюс та самая пустая строка.

> [p120] **The C++-inheritance machinery is
> format-blind — verified, not assumed (owner clause 2026-08-22,
> verbatim: «проверь что все механизмы "наследования как в C++" которые
> мы сделали для Markdown, точно так же работают и для XML, включая
> новый синтаксис секций и фактов. Если нет, это тоже нужно
> улучшить»).** The B-011 family — `#use spec://… as X` aliasing, `@!X`
> references, the qualified splice (rename-on-splice with every
> reference kept valid), hoist/elision stubs, de-substitution of covered
> units, rename tombstones, and the dynamic-STATIC case — must produce
> BYTE-IDENTICAL compiled closures whether a dependency is authored in
> Markdown or in dialect XML (generic and named shapes both). Pinned by
> a twin-test family in vibe-spec's pipeline: each mechanism runs over
> an MD twin and its to_xml serialisation, outputs compared
> byte-for-byte; mixed trees (one dep MD, one XML) ride the same pins.
> Gaps found by the twins are fixed in the machinery, never by relaxing
> the assert. Landed: eight twins (four in vibe-spec's pipeline — alias +
> `@!X`, an alias declared inside the projected node over a mixed tree,
> the three-way same-short-anchor splice, fact-grain through an alias —
> comparing lane AND rename map; four on the bootgen floor — the
> static-transitive zone, single-copy hoist, de-substitution over a mixed
> lane, and the dynamic-STATIC install case), every twin minting its XML
> at run time so the family always carries the live dialect form. Parity
> held out of the box at the compile floor: byte-identical, no machinery
> change. The twins NAMED the lawful residue at the install floor —
> `vibe:static` provenance comments cite the true source file (extension
> included) and INDEX raw-snippet paths carry the materialised extension,
> while the dynamic-STATIC target stays the generated, extension-stable
> `STATIC.md` — honest provenance, not a format leak.
>
> <spec://org.vibevm.core/vibevm/common/PROP-045#INHERITANCE-PARITY>

> [p121] **`markdown` target:** XML sources emit as Markdown through
> the pivot (nested sections → heading levels, facts → anchored units,
> fences/tables/quotes → their MD forms); MD sources copy verbatim. This
> target exists for tooling that cannot read XML or mixed trees — named in
> the mandate as load-bearing, and it stays supported for as long as this
> PROP stands.
>
> <spec://org.vibevm.core/vibevm/common/PROP-045#TARGET-MD>

## Что появилось на диске {#what-appeared}

[p122] Ваш пакет живёт под `vibevm/vibepacks/org.acme/review/v0.1.0/`: манифест, README и три документа XML, которые правите вы. Копия vibe живёт под `vibevm/vibedeps/org.acme.review/0.1.0/` вместе с записью `.vibe-slot.toml` и переписывается при каждой установке. В `vibevm/vibespecs/boot/` лежат скомпилированный `STATIC.xml` и `INDEX.md`, чья строка `static` называет скомпилированный файл, а рядом — два ваших стартовых файла, которых ни одна установка не трогает. `vibe.lock`, [лок-файл](../glossary/index.xml#lock-file) проекта, закрепляет за пакетом его версию и хэш.

> [p123] **Decision.** A node's authored `spec/` and its materialised dependencies live in physically separate trees. `vibe install` **never writes into any node's authored `spec/`**.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-009#TWO-TREES>

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

[p124] При `spec_format = "xml"` раздел источника не должен повторять якорь контракта, а заголовок заметки не несёт якоря. Компилятор сливает разделы с общим якорем, и результат слияния сегодня не компилируется в `STATIC.xml`. Установка останавливается с `fact id … is defined twice`; копия пакета уже на диске, а лок-файл не записан. Два документа, чьи заголовки оба `{#root}`, сталкиваются так же. При `spec_format` по умолчанию тот же пакет компилируется.

[p125] Точка внутри якоря — путь, а не символ. К `{#verification.timeout}` нельзя обратиться, тогда как `#verification.timeout` доходит до раздела `timeout`, вложенного в раздел `verification`. Якорь — это буква, за которой идут буквы, цифры, `_` и `-`.

> [p126] `#<anchor>.<sub>…` is a **tree path** into the document IR (§5).
>
> <spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-035#URI-TREE-PATH>

[p127] `vibe init package` пишет `link = "dynamic"`, какой бы `--link` вы ни передали. Задайте связь в манифесте, как делает шаг 2.

[p128] При `link = "dynamic"` скомпилированного файла нет. `INDEX.md` называет саму заметку, и агент, который её откроет, встретит строку `#use` и должен будет сам по ней пойти.

[p129] `vibe explain` отвечает только за пакет, который несёт [карту прослеживаемости](../glossary/index.xml#traceability-map) (*traceability map*). Про этот он так и говорит и останавливается.

[p130] `vibe facts check` ловит опечатку в состоянии в форме элемента, `state="finished"`, и называет значение. В короткой форме `@status:spec/finished` вовсе не читается как маркер, и файл проходит как чистый. Состояния — `plan`, `work`, `done`, `hold` и `void`; стадии — `idea`, `spec`, `impl`, `test`, `doc`, `freeze` и `unknown`.

> [p131] One XML-shaped element, embedded in Markdown (and, later, native in XML
> documents — the frontend duality of PROP-035 §5):
>
> <spec://org.vibevm.core/vibevm/modules/vibe-facts/PROP-043#STATUS-ELEMENT>

