Продвинутые Markdown и XML
01Эта страница берёт пакет, который вы пишете в Markdown, и показывает XML, который вместо него читает ваш агент. По дороге вы узнаете, зачем VibeVM превращает каждый текст в XML и почему автору это ничего не стоит. Узнаете, как назвать правило так, чтобы машина могла на него указать. И научитесь делить длинный текст на короткий заголовочный файл и длинное тело — так программисты на C и C++ делят библиотеку. Вы соберёте один маленький пакет руками, посмотрите, как он компилируется, и докажете, что две формы — одно и то же. Заложите около получаса; агент не понадобится.
Что понадобится
| Что | Зачем | Где взять |
|---|---|---|
vibe |
создаёт проект, конвертирует текст и компилирует его | Установить vibe |
| текстовый редактор | три коротких файла вы пишете руками | любой |
| около получаса | весь путь, без агента |
Почему всё становится XML
03В проекте, который об этом просит, каждый текст, принесённый пакетом, ложится на диск как XML, в какой бы форме его ни написал автор. Эти тексты — спецификации: правила, которые пакет задаёт агентам, работающим под ним; а причина конверсии — читатель. Модель, которая читает <TESTS-FIRST fact="true" status="spec/done">, знает, где правило начинается, где кончается, как называется и в каком оно состоянии, не угадывая по вёрстке. Проза заставляет модель выводить все четыре ответа самой.
04Измерения согласны с интуицией. В эталонном исследовании Юань Суй с коллегами дали GPT-3.5 и GPT-4 одни и те же таблицы, записанные шестью способами: от простого текста с разделителями до CSV, JSON, XML, HTML и Markdown (WSDM 2024). На семи видах вопросов разметка, называющая свои части, обошла тот же текст в прозе. Лучше всех оказался HTML — на 6,76 процента по цифре самой статьи. В одной задаче, где нужно сказать, где начинаются и кончаются части таблицы, XML набрал 96,00 процента против 93,00 у простого текста и 92,33 у Markdown. Исследование про таблицы, и ровно до этого места страница его и доводит.
05Совет производителя говорит то же с другой стороны. Руководство Anthropic по промптам для Claude — рекомендация, а не исследование — советует оборачивать каждый вид содержимого в свой XML-тег, чтобы модель разбирала длинный промпт без двусмысленности (руководство). Олег Чирухин, автор VibeVM, обнаружил это на собственной практике раньше, чем об этом сказала хоть одна опубликованная работа. Цель xml — форма, в которой живут проекты этого руководства, и та, которую спецификация называет будущей основной.
06 spec: xml target
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.
07Имена значат не меньше скобок. Раздел становится элементом, названным по себе самому, <tests-first title="Tests before fixes">. Правило становится элементом, названным по своему идентификатору, с одним опознавательным атрибутом, fact="true", так что читатель, ничего не знающий о вашем словаре, находит каждое правило одной проверкой. Первый читатель этого диалекта — агент, а тегу, который говорит, что в нём лежит, легенда не нужна.
08 spec: Decision (ADR-part; owner ruling 2026-08-22, verbatim)…
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.
09 spec: Decision (ADR-part; owner ruling 2026-08-22, verbatim)…
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 isfact(the generic form, which stays in the dialect) or it carriesfact="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 isspecdoc/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 withfact="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-insensitivexml-prefixed name, soXMLBOOTcannot be an element — the predicate refuses it and the generic form carries such ids.
10Одно предостережение для тех, кто обучает модели. Свидетельства выше — про текст, который модель читает. Для текста, который модель пишет, всё наоборот: принуждение ответа к JSON, XML или YAML может стоить качества рассуждений (Тэм с коллегами, EMNLP 2024). VibeVM задаёт форму тому, что агент читает, и никогда — тому, что он отвечает, так что этот результат его не касается.
Markdown и XML: одна модель под капотом
11Под капотом две формы — одна сущность. Каждый документ, Markdown или XML, разбирается в одно дерево: заголовок, статус и разделы, вложенные по глубине заголовков. Внутри разделов лежат абзацы, списки, таблицы, блоки кода и цитаты, и некоторые из них несут факт — одно именованное правило со статусом. vibe никогда не переписывает текст Markdown в текст XML. Он разбирает в дерево и печатает из него, в любую сторону.
12 spec: Decision (ADR-part)
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.
13Авторы компиляторов называют такое дерево промежуточным представлением, IR: единственная форма, в которую разбирает каждый фронтенд и из которой печатает каждый бэкенд. Компилятору для трёх языков и четырёх процессоров нужны три фронтенда и четыре бэкенда, а не двенадцать переводчиков, и правило, один раз сформулированное о дереве, действует для всех языков. У VibeVM та же форма: два фронтенда и два бэкенда, Markdown и XML с каждой стороны.
14 spec: Everything downstream operates on a single…
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.
15Дерево нужно ему по тем же двум причинам. Без него каждой паре форм понадобился бы свой конвертер, а конвертеры расходятся. Обзор кода в 2026 году нашёл внутри vibe четыре читателя Markdown, каждый со своим чуть иным диалектом: болезнь, от которой одно общее дерево и должно лечить. И каждый инструмент, читающий спецификацию, читает XML через то же дерево: проверка фактов, компилятор, который собирает список чтения агента, маршрутизатор, который разрешает адрес. Поэтому документ в XML и его двойник в Markdown дают каждому инструменту один и тот же ответ.
16 spec: Decision (ADR-part): scanners read XML through…
Decision (ADR-part): scanners read XML through its canonical Markdown projection — one dispatch layer above the parser, no dependency cycle.vibe-specdocdepends onprogress-core(its MD frontend is the adapter), so progress-core cannot itself call specdoc. The consumers dispatch instead: a.xmlspec entering any scanner (progress, check, specmap-host, show) is first projectedfrom_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).
17У дерева есть будущее за пределами конверсии. Компилятор называет его уровни — от текста одного документа до всего достижимого замыкания проекта, — и плагин компилятора может их читать и переписывать. Агент с длинным горизонтом когда-нибудь сможет планировать поверх этого замыкания: не стена текста, а граф именованных единиц с адресами. Это направление, а не возможность, которую можно запустить сегодня. И сам VibeVM — не агент. Он никогда не читает ваши правила, чтобы по ним действовать; он готовит текст, дерево и адреса для того агента, которого запускаете вы.
18 spec: The compiler's IR is multi-level…
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.
19 spec: Decision (owner ruling 2026-08-25; implemented through R6.5-D)
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 throughvibe extensions compile; D2 commissions a genuine installed dependency whose first-class TXT frontend feeds its JSON backend. Evidence:ee7f6f2d,def9909a,56307492.
20Вывод для вас прост. Пишите спецификации своих пакетов в Markdown — в форме, с которой уже справляются ваш редактор и ваши рецензенты. Каждое свойство формы XML придёт само: именованные элементы, факты, которые проверяет машина, адрес у каждого правила. Шаги ниже делают ровно это и в конце это доказывают.
Шаг 1: проект, который материализуется в XML
211. Создайте проект в пустой папке, как на странице Создать первый проект. Имя станет именем папки:
vibe init review-lab
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)
232. Откройте review-lab/vibe.toml, манифест проекта, и добавьте одну строку под [project]:
24[project]
spec_format = "xml"
name = "review-lab"
253. Дальше работайте внутри папки: cd review-lab.
26Эта строка решает, в какой форме ляжет каждый текст, который приносит пакет. С xml vibe конвертирует то, что автор написал в Markdown, пока копирует пакет в проект, а XML копирует как есть. Ваши собственные файлы не конвертируются никогда. Без этой строки каждый файл сохраняет форму, в которой его написал автор.
27 spec: The setting and its home (revised…
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, besideslot_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).mixedis 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.
Шаг 2: создать заготовку пакета
291. Добавьте в проект пакет. Это flow, пакет рабочих правил для агента, и он просит формат normal, смысл которого объясняет шаг 4:
vibe init package org.acme/review --kind flow --format normal
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)
312. Откройте манифест, который написала заготовка:
cat vibevm/vibepacks/org.acme/review/v0.1.0/vibe.toml
[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"
333. Измените две строки: дайте пакету description и поставьте link = "static". Конец файла станет таким:
34description = "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"
35Тип связи (link type) говорит, как текст пакета доходит до агента. При static vibe компилирует текст в тот единственный файл, который агент читает первым, целиком, в начале каждой сессии. При dynamic, выборе заготовки, он перечисляет файл, чтобы агент открыл его сам. Эта страница берёт static, чтобы вы увидели компилятор за работой.
36 spec: link = "static" — the contribution's…
link = "static"— the contribution's boot text is compiled intoSTATIC.mdahead 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.
37 spec: link = "dynamic" — the default.…
link = "dynamic"— the default.viberesolves the contribution to a concrete path inINDEX.md; the agent reads it dynamically, on demand. An optionalwhencondition gates the read: with awhenit is a conditional INCLUDE (loaded only when the condition holds) — mechanically the subskilllazy-pulldelivery mode; without one it is read unconditionally. Thewhendraws on the subskill[activation]probe vocabulary (PROP-003 §2.5) — one probe grammar across both mechanisms. v1 implements theos: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.
Шаг 3: якоря и факты, в Markdown
38Пакету нужны два документа. Первый — заметка, которую агент читает в начале сессии, стартовый фрагмент (boot snippet) пакета. Второй — контракт: сами правила, каждое со своим адресом.
391. Замените заметку заготовки, vibevm/vibepacks/org.acme/review/v0.1.0/vibevm/vibespecs/boot/10-flow-review.md, на эту:
40# 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
41Строка, начинающаяся с #use, — директива: инструкция компилятору, а не проза. Она втягивает контракт перед заметкой, так что агент встречает правила раньше заметки, которая на них ссылается. Файлы этой страницы написаны по-английски, как и вывод команд, чтобы примеры совпадали с тем, что вы увидите у себя.
422. Создайте vibevm/vibespecs/contract/REVIEW.md в том же пакете:
43# 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
44Прочитайте файл так, как читает машина. Заголовок несёт якорь в фигурных скобках, {#one-idea}: имя, которым пользуется ссылка, часть адреса после #. Абзац, который открывается @fact:ONE-IDEA, — факт, одно заякоренное правило со статусом. @status:spec/done в его конце говорит, что правило решено и ещё не построено. Элемент <status> под заголовком — тот же маркер для всего документа. Якоря заголовков и идентификаторы фактов делят одно пространство имён, поэтому ни один не повторяется в документе дважды.
45 spec: Fact anchors — the anchored-when-marked law
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.
46 spec: Every unit that carries a status…
Every unit that carries a status marker — paragraph or list item — MUST also carry a@fact:<ID>anchor; a marked, anchor-less unit is acheckerror.
47 spec: @status:<stage>/<state> and @status:<stage> are macro-equivalents…
@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.
48 spec: <ID> is [A-Za-z][A-Za-z0-9_-]*; the unit…
<ID>is[A-Za-z][A-Za-z0-9_-]*; the unit is then addressable asspec://…/<doc>#<ID>, sharing one address space with the heading{#anchor}s — a duplicate across both forms is acheckerror. The address is unchanged by the spelling: it names the id, never the opener.
49Регистр идентификатора — сигнал. Идентификаторы в верхнем регистре помечают правила с обязывающей силой; в нижнем — заголовки, вводные и заметки. Адрес первого правила — spec://org.acme/review/contract/REVIEW#ONE-IDEA. Он складывается из координаты пакета, пути документа под vibevm/vibespecs/ без расширения и якоря. Адрес не меняется, когда файл меняет форму.
50 spec: Decision — two anchor-id registers (owner ruling, 2026-07-24)
Decision — two anchor-id registers (owner ruling, 2026-07-24).@fact:UPPER-SLUGnames a normative fact (a law, rule, carrier, changelog entry — content with binding weight);@fact:kebab-casenames a service unit (status lines, lead-ins, connective prose).
51 spec: spec:// addressing is format-blind: anchors…
spec://addressing is format-blind: anchors areidattributes 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.
523. Проверьте разметку пакета:
vibe facts check --path vibevm/vibepacks/org.acme/review/v0.1.0
progress check: clean (2 files, 0 warning(s))
54Проверка читает каждый документ под vibevm/vibespecs/ пакета. Маркер на абзаце без якоря и идентификатор, определённый дважды, — ошибки, которые называют строку.
554. Установите пакет в его собственный проект:
vibe install org.acme/review --assume-yes
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).
57Установка копирует пакет в vibevm/vibedeps/, конвертируя оба документа в XML. Она же компилирует стартовую полосу (boot lane) — упорядоченный список файлов, которые агент читает в начале сессии. Последние две строки диффа — это она: в INDEX.md появилась строка с именем скомпилированного файла, а STATIC.xml возник.
585. Откройте контракт таким, каким его прочитает агент:
cat vibevm/vibedeps/org.acme.review/0.1.0/vibevm/vibespecs/contract/REVIEW.xml
<?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>
60Каждая часть названа по себе самой. Раздел {#one-idea} стал элементом <one-idea> с заголовком в атрибуте. Факт стал <ONE-IDEA fact="true" status="spec/done">. Префикс @fact: и суффикс @status: из текста исчезли: они были написанием, а дерево хранит только смысл. Строчная разметка Markdown — обратные кавычки, ссылки — едет внутри текста без изменений.
61 spec: Decision (ADR-part)
Decision (ADR-part). Inline content — emphasis, inline code, links,##NAMEcitations,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.
Заголовочный файл и его реализация
62Контракт, который говорит всё, обходится дорого. Каждое слово стартовой полосы читает каждый агент в начале каждой сессии. Поэтому текст, который должен быть перед глазами всегда, хочет быть коротким, а рассуждение за ним хочет лежать там, куда агент дотянется, когда спросит. C решил задачу такой формы в 1970-е двумя файлами, и C++ сохранил это решение. Заголовочный файл, .h, в нескольких строках объявляет, что предлагает библиотека; единица трансляции, .c или .cpp, несёт реализацию. Все, кто пользуется библиотекой, включают заголовок, и никто не вставляет реализацию в собственный код.
63 цитата из спецификации
Inspired by C/C++.h/.cpp:
64VibeVM заимствует это разделение для текста. Пакет в формате normal держит под vibevm/vibespecs/ две папки. contract/ — заголовок: маленький, дешёвый в загрузке, поверхность, которую видят другие пакеты и агенты. source/ — реализация: тяжёлое тело, которое втягивается только тогда, когда кто-то попросит. Компилятор C видит всю программу и сам сводит объявления с определениями; у vibe такого взгляда на ваш текст нет, поэтому контракт сам называет свою реализацию директивой #source. У формата по умолчанию, simple, ничего этого нет: такой пакет несут целиком и читают потому, что он есть.
65 spec: contract/: — small, simple, boot-snippet-like.…
contract/ — small, simple, boot-snippet-like. The surface a package exposes outward; short files, cheap to load. The analogue of a header.
66 spec: source/: — large, heavy. The full…
source/ — large, heavy. The full implementation; pulled only when actually needed. The analogue of a translation unit.
67 spec: Because the structural executor lacks…
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.
68 spec: format = "simple"
format = "simple"— the default (absentformat, a package issimple). 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].sourcenames 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 adoptingnormal.
69 spec: format = "normal"
format = "normal"— the VibeVM-native form, opt-in: thecontract/sourcesplit (§4), directives (§7), and the compiler (§8). Anormalpackage is not read just because it is present — it participates only when something actually#uses it (§7.2). This is tree-shaking; the optimized posture for authors who understand the machinery, at the price of structuring the package correctly.
70Спецификации называют этот механизм наследованием, как в C++: один документ строится на другом, не копируя его текст. Втягивают две директивы, и с одной вы уже знакомы. #use называет документ или раздел, который нужно прочитать раньше текста, который им пользуется; компилятор копирует его вперёд, и копия приводит с собой всё, что втягивает сам скопированный документ. #source называет реализацию контракта, и компилятор компилирует её вслед за контрактом. В скомпилированном файле обеих директив уже нет — они израсходованы. Третья, #embed, вклеивает ровно один адресованный узел на то место, где стоит: макрос, а не include.
71 spec: Inline mode
Inline mode. The same, statically: the#used library's text is fully copied higher up inSTATIC.mdso it is available before the user.
72 spec: Like a C++ interface, but…
Like a C++ interface, but with section-level merging.contractsections are the exposed surface;#sourcenames 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.
73 spec: #embed has arbitrary granularity
#embed has arbitrary granularity — it splices exactly the addressed node, no more.
Шаг 4: отделить контракт от его причин
741. Создайте в пакете vibevm/vibespecs/source/details.md, реализацию контракта:
75# 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
76Каждый якорь здесь отличается от якорей контракта: one-idea-why рядом с one-idea; особые случаи ниже объясняют почему. Последний раздел — список, в котором каждый пункт — факт.
772. В contract/REVIEW.md добавьте одну строку после первого абзаца:
78#source spec://org.acme/review/source/details
79Адрес называет целый документ, без якоря, так что компилятор берёт его от заголовка и ниже.
803. Установите снова:
vibe install org.acme/review --assume-yes
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).
82Ни один пакет не добавлен, поэтому полоса — единственная строка диффа: скомпилированный файл вырос на реализацию.
834. Спросите vibe, что агент читает первым:
vibe tree --plain
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
85Один пакет, связанный как static, и x говорит, что его текст скомпилирован в STATIC.xml.
865. Откройте скомпилированный файл:
cat vibevm/vibespecs/boot/STATIC.xml
<!-- 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>
88Читайте сверху. Сначала комментарии: правила, по которым агент трактует метки в этом файле, затем таблица всех переименованных якорей. XML запрещает два дефиса подряд внутри комментария, поэтому -- в метке там записано как -%2D; элементы ниже несут настоящие --. Затем один документ XML: сначала контракт, за ним реализация, вкомпилированная одним вложенным разделом. Последней идёт заметка из фрагмента — простой <section>, потому что у её заголовка не было якоря. Строк #source и #use больше нет.
89 spec: In source, absent in contract
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 noprivate/publicaccess control.)
90Каждый якорь теперь несёт префикс org-acme--review-- — метку своего происхождения. Два пакета, у которых раздел назван root, не столкнутся в одном файле, а таблица наверху говорит, чем стало каждое короткое имя. Ссылайтесь на исходный документ и никогда на этот файл: это кэш, который меняется всякий раз, когда меняется пакет.
91 spec: Compiled labels are origin-qualified (B-011)
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.
92 spec: A generated STATIC.md is not a citation target
A generatedSTATIC.mdis not a citation target — authored text never citesspec://…/boot/STATIC#…; the lane is compiler output, and source-of-truth is the package source undervibedeps/(PROP-035 §11's lint, B-011 §6.1).
936. Откройте запись, которую vibe хранит рядом со своей копией пакета:
cat vibevm/vibedeps/org.acme.review/0.1.0/.vibe-slot.toml
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"
95spec_format называет цель, converter_recipe — версию конвертера. Каждая строка говорит, был файл скопирован (copied) или сконвертирован (converted), и несёт хэш того, что легло на диск. Держите этот файл в уме: шаг 5 прочитает его снова.
96 spec: The hash law under transformation
The hash law under transformation. Source identity is unchanged: lockfilecontent_hashand 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, versionedconverter_recipe, optionaloverlay_hash,derived_hash, and per-file source/output/disposition/SHA-256 rows. The legacy.vibe-derived.tomlis 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 andderived_hash. A missing legacy record triggers one final migration, a malformed new record refuses, and mismatched valid state rematerialises through the owned diff; changingspec_formatcan never earn a presence skip. Semantic equivalence remains the converter's proof through the shared IR, never the hash's job.
Эквивалентные формы
97Конструкции, которыми пользовалась эта страница, бок о бок, из её собственных файлов:
| 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:// |
те же символы внутри текста |
99Диалект закрыт и мал нарочно. Он выражает ровно то, что выражает Markdown, поэтому конверсия в любую сторону не теряет смысла, а элемент вне диалекта — громкая ошибка, а не молчаливый пропуск. У двух якорей нет собственного элемента: у того, что начинается с цифры, и у того, что совпадает со словом самого диалекта, таким как title или list. Для них встают общие <section id="…"> и <fact id="…">, и каждый читатель принимает оба написания. Единственное исключение из правила эквивалентности — словарь пакетов документации, к которым принадлежит и это руководство: у их выполняемых примеров и живых цитат нет формы в Markdown, и в Markdown они проецируются только в одну сторону.
100 spec: Decision (ADR-part)
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 kinddoc, and one-way to Markdown by law; for the spec vocabulary this decision stands unchanged.
101 spec: Decision (ADR-part; owner ruling 2026-08-22, near-verbatim)…
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.factsjoins the reserved vocabulary (an anchor namedfactsfalls back to the generic form);orderedcarries over exactly as on<list>; the model is unchanged — both shapes parse to the sameBlock::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 bumpsspecdoc/3→specdoc/4and the host re-materialises once. Landed: the writer branch, thefacts_blockparser (split into the pivot's ownxml_facts.rsalong 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 atspecdoc/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.
102 spec: Decision (ADR-part; the recorded reopening of ##XML-DIALECT-IS-THE-MD-SUBSET, 2026-09-11)
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: 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 kinddocand projects to Markdown one way, by law, not by defect.
Шаг 5: сконвертировать и увидеть, что ничего не изменилось
103Вы написали Markdown и отгрузили XML. Последний шаг показывает, что XML, который вы написали бы руками, — тот же файл, байт в байт.
1041. Спросите конвертер, что потеряет конверсия:
vibe refactor convert-source --to xml --dry-run vibevm/vibepacks/org.acme/review/v0.1.0/vibevm/vibespecs
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
106Конвертер разбирает каждый файл в дерево, печатает XML, читает его обратно и снова печатает Markdown, а затем сравнивает. Вердикт ir-stable-loss значит, что дерево уцелело, а байты нет, и дифф показывает, что изменилось. Здесь это одна пустая строка в конце каждого файла, которую добавляет принтер Markdown. Без --force команда отказывает файлу из-за такой потери. В изменении смысла она отказывает всегда, потому что это был бы дефект конвертера, а не вашего файла.
107 spec: The destructiveness check is the owner's…
The destructiveness check is the owner's law, implemented by reverse reconversion through the pivot: for a sourceS, 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 equalsSbyte-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,--forcedoes not apply; that class is avibe-specdocdefect to file (the pivot broke its own round-trip law), never a corpus to damage.
108 spec: On a TTY, class-2 files prompt…
On a TTY, class-2 files prompt per file (yes / no / all); off a TTY, class-2 without--forceis an error listing every lossy file and what each loses.--forcewaives class 2 only.--dry-runclassifies 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.
1092. Сконвертируйте три документа. Команда пишет каждый .xml рядом с его .md и удаляет .md тем же действием. Дерево никогда не держит документ в обеих формах:
110vibe refactor convert-source --to xml --force vibevm/vibepacks/org.acme/review/v0.1.0/vibevm/vibespecs
111 spec: A conversion writes the sibling serialisation…
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 holdsX.mdandX.xmltogether, not even transiently between files. The verb assumes version control underneath it and keeps no backups of its own.
1123. В манифесте пакета направьте source на новый файл, vibevm/vibespecs/boot/10-flow-review.xml.
1134. Установите снова:
vibe install org.acme/review --assume-yes
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).
1155. Откройте запись ещё раз:
cat vibevm/vibedeps/org.acme.review/0.1.0/.vibe-slot.toml
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"
117Сравните её с записью шага 4. Каждый из трёх документов теперь copied, и хэш каждого — тот же, что был у него как у converted. То, что конвертер написал из вашего Markdown, — это то, что из него написала установка, байт в байт. Скомпилированный файл тоже не изменился:
vibe tree --plain
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
119Те же байты, те же строки. Вот что значит на практике одна модель под капотом. Форма, в которой вы пишете, — ваш выбор; текст, который доходит до агента, один и тот же. Проект с spec_format = "markdown" проходит ту же дорогу в обратную сторону. Markdown, который он пишет из вашего XML, — ваш исходный файл плюс та самая пустая строка.
120 spec: The C++-inheritance machinery is format-blind —…
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 Xaliasing,@!Xreferences, 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:staticprovenance 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-stableSTATIC.md— honest provenance, not a format leak.
121 spec: markdown target
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.
Что появилось на диске
122Ваш пакет живёт под 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, лок-файл проекта, закрепляет за пакетом его версию и хэш.
123 spec: Decision: A node's authored spec/…
Decision. A node's authoredspec/and its materialised dependencies live in physically separate trees.vibe installnever writes into any node's authoredspec/.
Особые случаи и правила
124При spec_format = "xml" раздел источника не должен повторять якорь контракта, а заголовок заметки не несёт якоря. Компилятор сливает разделы с общим якорем, и результат слияния сегодня не компилируется в STATIC.xml. Установка останавливается с fact id … is defined twice; копия пакета уже на диске, а лок-файл не записан. Два документа, чьи заголовки оба {#root}, сталкиваются так же. При spec_format по умолчанию тот же пакет компилируется.
125Точка внутри якоря — путь, а не символ. К {#verification.timeout} нельзя обратиться, тогда как #verification.timeout доходит до раздела timeout, вложенного в раздел verification. Якорь — это буква, за которой идут буквы, цифры, _ и -.
126 цитата из спецификации
#<anchor>.<sub>… is a tree path into the document IR (§5).
127vibe init package пишет link = "dynamic", какой бы --link вы ни передали. Задайте связь в манифесте, как делает шаг 2.
128При link = "dynamic" скомпилированного файла нет. INDEX.md называет саму заметку, и агент, который её откроет, встретит строку #use и должен будет сам по ней пойти.
129vibe explain отвечает только за пакет, который несёт карту прослеживаемости (traceability map). Про этот он так и говорит и останавливается.
130vibe facts check ловит опечатку в состоянии в форме элемента, state="finished", и называет значение. В короткой форме @status:spec/finished вовсе не читается как маркер, и файл проходит как чистый. Состояния — plan, work, done, hold и void; стадии — idea, spec, impl, test, doc, freeze и unknown.
131 spec: One XML-shaped element, embedded in Markdown…
One XML-shaped element, embedded in Markdown (and, later, native in XML documents — the frontend duality of PROP-035 §5):