PROP-057 — Documentation packages and the documentation site
01spec://org.vibevm.core/vibevm/common/PROP-057
02Status: contract authored, not yet implemented. This PROP is the norm for two new package kinds (doc, app), the companion role of documentation packages, the relation between a documentation package and its subject, localization by package, discovery and the official/community hierarchy, the package card, the two documentation levels, the public site vibevm.org/doc, the Rust content pipeline, the local reader, the web stack, the SEO contract, observability without judging, the fate of the old docs/ tree, and the style law of the documentation prose. Every decision below was taken by the owner in the campaign vision; where a decision is still a proposal, its facts carry spec/work.
03Related: PROP-000 §6 (##KIND-SET, ##INV-VOCABULARY — the kind register mirror), PROP-028 (the family roles this PROP extends with the -docs companion), PROP-045 (the XML dialect this PROP widens with a documentation vocabulary), PROP-043 (the audience vocabulary gaining agent), PROP-047 (##DOC-COVERAGE-RATCHET, ##DEBT-MUST-BE-ASKABLE), PROP-003 §2.7 (BCP-47 tags and the preference chain the localization reuses), PROP-024 (a package is its own source tree — no build artifacts), PROP-044 §6.2 (##FMT-MANIFEST — the manifest is the most expensive format), PROP-052 (the vibevm/ root the pages live under), PROP-019 (the ~/.vibe/opt/vibevm/ store the shell asset joins), PROP-025 (release assets and their manifest), PROP-009 (the boot lane documentation never enters), the design rationale design/documentation-vision.xml (the campaign vision with its thirty decision records — this PROP wins where they disagree), and the campaign zone campaigns/docs-2026-09/ (plan, ledger, findings, journal).
1. Owner mandate, verbatim
04The owner opened the campaign (2026-09-09): «я хочу следующим этапом начать писать документацию. И я хочу понять, как правильно ее писать, чтобы она а) хорошо анализировалась ИИ агентами б) идеально читалась людьми». The same sitting named the three abilities the documentation owes: jumping from a document to the exact rule of a specification, «как часть делают в книгах по C++, где упрощенное описание в учебной книге ссылается на точные строки стандарта C++»; examples that let a user understand a feature without long thought; and an infrastructure for agents «и через веб, и через локальный доступ», so that a future skill can send an agent to the documentation as well as to the specs.
05The owner set the shape of the site (2026-09-09): «сделать аналог docs.rs который генерирует документацию для ВСЕГО что лежит в репозитории — это очень круто и очень хотелось бы сделать так. Но важно, что пакеты в центральном репозитории vibespecs меняются и технически сайт должен уметь это всё обновлять у себя. Возможно, опциональная документация должна идти сопроводительным пакетом типа org.vibevm.world.docs/multi-user-planning (или выработать еще какую-то конвенцию более правильную, если эта не подходит)».
06The owner's five points (2026-09-09): «1) вводим kind doc 2) пакет org.vibevm.doc/web, сайт vibevm.org/doc 3) было бы неплохо в будущем сделать этот сайт запускаемым и локально, чтобы потом сделать приложение для чтения документации (например, плагин для vscode как встроенный iframe на это локальное приложение) — это нужно для чтения документации закрытых проприетарных пакетов, которых нет в репозитории 4) всё должно быть сделано для максимального SEO, включая LLM SEO для того чтобы краулеры Anthropic и OpenAI находили быстрее: llms.txt, llms-full.txt для базового корпуса документации, и другие приёмы 5) технически стек для веба — https://qwik.dev, а точнее — его свежая версия 2.0: https://next.qwik.dev/ (да, она в бете, это нормально)».
07On the host channel (2026-09-09): «канал хоста — пока что только тот репозиторий который мы указали в настройках при запуске/генерации (по умолчанию — гитхаб), зеркала и прочее — когда-нибудь в будущем».
08On officiality (2026-09-09): «я боюсь что если ребро документации будет исходить из самого пакета, то три разных человека законтрибьютят три разных пакета документации, и непонятно будет — какой "официальный" пакет показывать на нашем сайте. Может быть, ребро должно исходить и из самого документируемого пакета тоже? То есть, пакет указывает свою "официальную" документацию (и там может быть одна штука выбрана как "основная" документация, и сколько угодно как дополнительные "официальные"). Но при этом остается возможность самим пакетам с документацией сделать обратное ребро тоже — и тогда на сайте мы сможем сделать раздел с "неофициальной" документацией (community docs)».
09On localization (2026-09-10): «скорей всего, нужно каждую из локализаций иметь отдельным пакетом, а сайту показывать селектор локализации. А в пакете иметь официальную ссылку на каждый из пакетов для разных языков», and: «неофициальные переводы тоже должны искаться сайтом (просто отображаться как переводы сообщества, а не официальные). Эта иерархия официальной и неофициальной документации, их официальных и неофициальных переводов должна как-то понятно и наглядно отражаться в интерфейсе (например, звездочки на "официальных" элементах)».
10On the card (2026-09-10): documentation may carry its own human-readable name, «таким как оно будет выглядеть в интерфейсе сайта»; «обычно для библиотеки документации лучше иметь человекочитаемое название и человекочитаемый абстракт — так же как это делают поисковики по arxiv.org»; an optional large square icon and an optional wide banner «как баннер профиля в твиттере 1500x500», with generated placeholders when absent; and the Open Graph preview «отдельной опцией», because the banner's proportions do not fit a link preview.
11The owner's answers to the vision's forks (2026-09-10): «2) вариант Б, а приложения и VSCode-плагины будут вставлять в себя этот интерфейс через webview 3) сразу A, и спланировать самые частые вещи 5) вариант А, добавить agent 6) Б для локального читателя, А для сборки публичного сайта на сервере, где Node есть. Но прежде чем подтвержу, скажи как именно ты собрался встраивать оболочку vibe» — the last clause is why §12's embedding mechanism carries spec/work.
12On versions (2026-09-10): «мы очень редко обновляем версию Vibe. Узнать что версия изменилась нельзя почти никак, и это фича. […] Мы постоянно делаем trunk based development с переписыванием истории». And: «я предлагаю тебе не рассчитывать ни на что кроме самого номера версии. То есть, разница считается между номерами версий. […] Она происходит из осознанного желания владельца поменять номер версии». And: «эти данные — это всё нужно для разработчиков документации. А пользователи всей этой внутренней кухни видеть не должны. Они видят версию 1.0.0 и воспринимают это как контракт».
13On maintenance (2026-09-10): «Единственное что мне не нравится твое правило "продукт не выходит без обновления документации". Это неправда в нашем случае. Мы можем релизить новые версии 10 раз в день и мерджить по 100 пулл-риквестов в день. Нет никаких шансов, что документация не будет дрейфовать. Этот риск мы принимаем. Мы просто обещаем себе чисто исходя из процессов нашей команды (не технически) время от времени проводить полную проверку».
14On style (2026-09-10): «Мы пишем документацию для умных, технологически продвинутых людей, многие из которых — senior developers или имеют академический бэкграунд в ИИ. И часто даже они не понимают, что написала Claude. Потому что агенты Claude обычно пишут исходя из неверного предположения, что человек вначале прочитал всю документацию и все спеки […]. Поэтому технические места лучше описывать словами: ASD-STE100 Simplified Technical English (STE), открыто и просто говорить как делаются те или иные вещи (без "посмотрите в спецификацию, прочитайте все и сами поймете). […] писать для них нужно как в лучших научно-популярных журналах — ярко, броско и с юмором […]. Важно: исходный текст английский, все остальные языки (включая русский!) это адаптации английского».
15On the landing and the order of work (2026-09-10): «В ходе кампании нужно наш лендинг vibevm-org тоже переделать на Qwik чтобы было однообразно и хорошо композировалось»; «вначале сделай всю документацию на английском, русский перевод будет следующей волной»; «вначале написала все красивые тексты на английском, и дальше мы полностью переключимся на Опус и будем работать в нем над всей разработческой частью».
16On scenario pages (2026-09-10): «теперь любое действие можно сделать не только вручную, но и агентом. Поэтому для сценариев имеет смысл вначале писать, каким простым промптом достичь результата […], и только потом уже разворачивать механику работы без агентов целиком вручную (если это вообще нужно! иногда не нужно!). […] Это важное отличие от документации прошлого, где все делалось только руками».
2. Two new kinds: doc and app
17The kind register is VIBEVM-SPEC.md §4.1 and grows only by the owner's hand; PROP-000 ##KIND-SET and ##INV-VOCABULARY mirror it. This PROP prepares the amendment admitting doc and app and defines their semantics; the register entry itself is the owner's edit (campaign atom A1.1).
18doc — documentation as a package. A package of kind doc documents one or more other packages (its subjects) and is read, never executed.
- 19A
docpackage MUST declare at least one subject in[[documents]](§4) and MUST carrytitleandabstract(§7). - A
docpackage MUST NOT declare[boot_snippet],[[mcp_server]]or[[binary]];vibe checkrejects the manifest. Documentation never enters a boot lane (§14). - A
docpackage MAY declare[[skill]],[translates](§5) and[media](§7). - Its pages live under
vibevm/vibespecs/of its own tree like the specs of any package (PROP-052); a document belongs to the documentation genre by the kind of the package that contains it, and only there the documentation vocabulary of the dialect is open (PROP-045##DOC-VOCAB-BY-KIND). vibe installMUST refuse adocpackage with a hint naming the warm-up command; documentation is warmed into the machine store withvibe cache add(§11), and the local reader andvibe explainread the store.- Known limit: the store is per machine, not per project; a teammate who clones a project does not get the same documentation version automatically. The skill names the warm-up command; a project-level «consult this documentation» declaration is not part of this wave.
20Decision: documentation is a package kind, not a genre marker inside other kinds.
- 21Why: the owner's three requirements — local reading, a skill, a site — are met by one mechanism: local reading is a store warm-up, the skill is
[[skill]]in the manifest, the site renders the same bytes. Keeping documentation out ofvibedeps/keeps consumer trees thin and keeps tutorials out of an agent'sgrepover dependencies. The kind, not the document genre, drives behaviour because tools know the kind before they read a file: the index, the publish gate,vibe init,vibe list. - Considered and rejected: documentation as kind-less content (the shape of the earlier phase-G draft) — tools could not tell it from a
flowand could not forbid a boot snippet; materialisingdocpackages intovibedeps/by default — it bloats every consumer's committed tree; a fifth layout rootvibevm/vibedocs/— it breaks the one-module layout law of PROP-052 for no gain, since addressing, scanners and the pivot already work overvibevm/vibespecs/. - Revisit when: a consumer needs documentation in the committed project tree or reproducibly across a team's machines — observed as a BACKLOG request or a real
vibe installrefusal in the field. Then add a project-level declaration and a materialisation mode without changing the default.
22app — a standalone product. A package of kind app is a product with its own deployment profile in the build, package and deploy planes of PROP-054; the site package org.vibevm.doc/web is of kind app.
- 23The boundary with
toolis mechanical: atoollives in a project and runs throughvibe bin execby the lock file; anappruns nowhere in a consumer project and is built and deployed on its own. - Considered and rejected: treating the site as a
tool— a site has no meaning as «execute in the project by the lock file». - Revisit when: a second
appwith a different launch mechanism appears.
24In code the two kinds join the closed PackageKind enum and every exhaustive match grows a branch; a wildcard branch hiding a kind is forbidden. The index side (vibe-index, an open vocabulary with Unknown(_)) stays open — that is its own law. The behavioural wire vocabulary (package_kind in formats/vocabularies.json) widens through cargo xtask codegen, never by hand in the generated tree.
3. The companion role outside family unison
25Documentation and its translations are companions of the packages they document: they share a name stem for the default case of officiality and nothing else.
- 26The official-by-default documentation of a subject
<group>/<name>is named<group>/<name>-docs, in the same group. The core documentation isorg.vibevm.core/vibevm-docs, the companion of the host coordinateorg.vibevm.core/vibevm. - The official-by-default translation of a documentation
<group>/<docname>into a language<lang>is named<group>/<docname>-<lang>, where<lang>is a BCP-47 tag in lower case (ru,pt-br,zh-hans). - Any other documentation or translation may carry any name in any group: the naming convention exists only for officiality by default, and the displayed name is the card's
title(§7). - PROP-028 gains the role
<family>-docsas a companion: it does not take part in the family's unison versioning, it keeps its own version line, and it expresses compatibility with its subject through the version constraint in[[documents]](§4).
27Decision: a suffix in the subject's group, outside unison.
- 28Why: a suffix in the same group is already the family rule beside
-langand-mcp; no new naming grammar is needed. Only the group's owner can publish into the subject's group, so officiality cannot be forged through a name. Family unison is the law for a tested set of code; prose and translations move in a different rhythm. - Considered and rejected: a subgroup
org.vibevm.world.docs/<name>(the DefinitelyTyped shape) — it copies another owner's namespace by agreement, carries no subject version, does not work for third-party authors, and needs a new naming law; including-docsin family unison — every documentation edit would bump the family's code; encoding the language in the group (org.vibevm.core.ru/…) — the same subgroup mistake. - Revisit when: PROP-028 gains a fourth code role and suffixes get crowded — observed as a stem conflict in the registry.
4. The relation between documentation and its subject
29The relation is declared in both directions by manifest fields, not by name:
30# in a package of kind = "doc"
[[documents]]
package = "org.vibevm.world/multi-user-planning"
version = "^1.0"
# in the subject, of any kind
[documentation]
primary = "org.vibevm.world/multi-user-planning-docs"
official = ["org.vibevm.world/multi-user-planning-tutorials"]
- 31
[[documents]]is REQUIRED in adocpackage, may list several subjects, and itsversionis a semver constraint. [documentation]in the subject names coordinates without versions;primarynames at most one package,officialany number.- Official documentation is the one whose edges converge: the subject named the package and the package declared the subject. Community documentation has only the edge from the documentation. An edge from the subject alone reads «not published or an error», and the site shows a warning.
- Default convention: when the subject declares no
[documentation], the package<name>-docsin the same group counts as official and primary. A declared[documentation]replaces the convention entirely. - For a subject version V the site shows the documentation versions whose
[[documents]]constraint admits V, choosing the newest. - Warming a
docpackage withvibe cache addwarms its subjects too, so thatspec://citations resolve offline. - The relation fields enter the registry index entry (
title,abstract,documents,documentation,translates,media; theversion_entryvocabulary offormats/vocabularies.jsonwidens throughcargo xtask codegen), so that reverse links are built fromprimary.jsonland never by downloading packages. - Reverse questions — «who documents X», «which translations does Y have» — and officiality are computed on the site's side and in
vibe-docby one fold overprimary.jsonlin memory at build time; the index gains no new routes or folders, and the local reader takes the same path offline. - The subject
org.vibevm.core/vibevmof the core documentation is a project coordinate (the host root is[project], not a registry package —design/host-as-package.xml);vibe checkverifies the form of a subject coordinate and does not require the subject to be selectable from a registry; the site knows the host coordinate from its configuration (§9). - Officiality is never stored as a flag: it is computed from the convergence of edges at every render. A field
official = truein a manifest or an index is a design error. - Placement in the manifest:
[[documents]],[documentation],[translates]and[media]are top-level tables beside[boot_snippet]and[[skill]];titleandabstractsit inside[package]besidedescription. Recommended and drafted so; the owner's confirmation is open (vision §10, question 4).
32Decision: two edges, officiality by convergence, an unversioned pointer from the subject, a default convention by name.
- 33Why: the edge from the subject removes the owner's fear — three contributors cannot fight over «official», because the subject assigns it. The pointer carries no version because documentation almost always ships after the code. The default convention spares the subject a re-release for the obvious case. Precedent: the
documentationfield ofCargo.toml. - Considered and rejected: a capability
docs:<coordinate>inprovides— the capability grammar admits only kebab-case in both halves (crates/vibe-core/src/capability_ref.rs) and a coordinate does not fit; an ordinaryrequiresdependency on the subject — a dependency does not mean «I document»; an officiality mark in the registry index outside the package bytes — a second source of truth that vanishes in local mode and private registries; the name convention alone — zero guarantees. - Revisit when: a third relation of the same nature appears beside
documentsandtranslates— then generalise into one relation table instead of adding fields.
5. Localization: one package per language
34A translation of documentation is a separate package of kind doc:
35[package]
name = "vibevm-docs-ru"
group = "org.vibevm.core"
kind = "doc"
title = "Руководство VibeVM"
abstract = "…"
[i18n]
canonical = "ru" # the package's language — the existing PROP-003 field, a BCP-47 tag
[[documents]] # the same subject as the source
package = "org.vibevm.core/vibevm"
version = "^1.0"
[translates]
package = "org.vibevm.core/vibevm-docs"
version = "^0.3"
- 36The language of a
docpackage is the existing[i18n].canonicalof PROP-003 §2.7 (defaulten); there is no separatelangfield.[i18n].availableof adocpackage is empty by construction: a translation is another package. - The source stores no list of its translations. Which translations a documentation has, the site and the local reader compute from the
translatesedges at every render — exactly as officiality fromdocumentsanddocumentation. - The sidecar layout of PROP-003 §2.7.1 does not apply to
docpackages; the BCP-47 tags, the preference chain (I18nDecl,project_preference_chain,resolve_localised) and the lock file's[meta].language_chainare reused unchanged. - A translation MUST mirror the source tree file for file: the same paths, the same anchors, the same fact identifiers, the same number and kinds of blocks. Adding or removing an anchor is forbidden. A set difference of anchors is a check error, not a warning.
- A translation stores neither a revision nor a hash of the source page (§14: a «since then» comparison needs a history the product does not keep).
vibe doc check --translationsverifies structure only — paths, anchors, count and kinds of blocks; whether the adaptation lags in meaning is a human's question at full reconciliation, and the site shows the adaptation's last read-aloud date fromreviews.toml. - A translation MUST NOT author examples: it uses
example ref="<id>"pointing at the source's example, so that command output is checked once, on the source (PROP-045##ROW-DOCVOCAB-EXAMPLE-REF). - The
documentsof a translation MUST equal thedocumentsof its source;vibe checkverifies it. - An official translation is one that declared
translateson the source and is published by the same group as the source under the name<docname>-<lang>; everything else is a community translation (§6). - On the site the language is a path segment (§9), a selector stands on every page, the fallback to the source language is per page and marked, never a 404;
hreflangandx-defaultare emitted;llms.txtexists per language. - The local reader serves the same from the store; the preference comes from the project's
[i18n].preferredor a launch flag. - Normative specifications stay in the specification's language: a
ruleshows the rule's text in the source language with a mark; translating normative text is outside this wave.
37Decision: one package per language, a block-for-block mirror, examples by reference.
- 38Why: the owner's goals — different authors, different rhythms, officiality per language — are goals of ownership, and the unit of ownership in this project is the package. The mirror and reference-example rules take from the sidecar model what made it safe: matching anchors, per-page fallback, one source of command output.
- Considered and rejected: sidecar files inside one package (PROP-003 as is) — the package grows with every language, a translator needs rights on the package, any translation edit bumps the whole documentation, and coverage cannot see staleness; a hybrid «official as sidecar, third-party as packages» — two mechanisms; a translation declared by the subject — a subject re-release per language; own examples in translations — divergent outputs per language.
- Revisit when: a request to translate normative specs appears — observed as a translation package trying to mirror a spec tree instead of a documentation tree.
6. Discovery and the official/community hierarchy
39The site finds documentation and translations by edges: every package of kind doc in the index whose documents or translates points at a coordinate, from any group. A star appears only where the edge is confirmed from above.
| Level | Status | Who confirms |
|---|---|---|
| documentation of a subject | ★ primary · ★ official · community | the subject, through [documentation] or the <name>-docs convention in its own group |
| translation of a documentation | ★ official · community | the source documentation, through the <docname>-<lang> convention in its own group |
- 41All four combinations are visible: official documentation with an official translation; official with a community translation; community documentation with a translation its author named official; community documentation with a community translation. A star on a translation means «named by the author of this documentation», not «approved by the subject», and the tooltip says so in words.
- Three signals agree and never contradict: the star, the caption «official» or «community», and the order «primary, official, community».
- The publisher is always visible: the package's group is printed beside the title.
- The language selector shows every language found: starred first, then community, each with its publisher.
- The shelves on a package page — primary, official extra, community — repeat the scheme for documentation; inside a shelf the same marks serve translations.
- The machine mirror: the page manifest carries the status on both levels (
primary,official,community) and the language;llms.txtmarks official items so that an agent prefers them but sees the rest; the index carries the relation fields, the language,titleandabstract. - There is no moderation: the community shelf shows everything that was found; the protection against impersonation is the visible publisher; exclusion lists are deferred.
42Decision: completeness by edges, unambiguity by assignment from above, visibility by stars and order.
- 43Why: the owner's word — unofficial work must be found and shown as community, and the hierarchy must be plain to see.
- Considered and rejected: showing only official items; officiality marks in the index; moderation in this wave.
- Revisit when: the first abuse of the community shelf is observed as a complaint to a package owner.
7. The card: title, abstract, description, media
44The package manifest gains card fields; for kind doc title and abstract are REQUIRED, for the other kinds optional, and [media] is optional for all:
45[package]
title = "VibeVM Manual"
description = "The operator's guide to vibe: install, lifecycle, registries, agents."
abstract = """
What it covers, for whom, what it assumes known, what it leaves out.
Three to six sentences, one language — the package's own.
"""
[media]
icon = "media/icon.png" # square, 256–1024 px, PNG/JPEG/WebP, up to 256 KB
banner = "media/banner.jpg" # 3:1, recommended 1500×500, up to 1 MB
preview = "media/preview.png" # 1.91:1, recommended 1200×630, up to 1 MB
- 46
titleis the display name on shelves, in the selector, in the page heading and in the catalogue; uniqueness is not checked, identity stays the coordinate, and the publisher is shown beside it. descriptionstays the one-line subtitle for lists and the page's meta description;abstractanswers four questions — what it covers, for whom, what it assumes known, what it leaves out — and is bounded at about a thousand characters. The entry page does not restate the abstract; it inserts it throughderived kind="manifest-field".- Audiences and languages are not declared on the card: they are derived from the page markup and the
translatesedges. - A
docpackage MAY declareauthorshipin[package]:human,aiormixed— who wrote the prose the package carries. It is metadata of the document, kept for the reader who filters a shelf by it; it is never an attribution of the commits or of the repository, whose authorship law isspec://org.vibevm.core/vibevm/common/PROP-000#commits. Absent means unknown: the site shows no badge and a filter by authorship leaves the package out of both named groups. - Images are source files in the package tree, in PNG, JPEG or WebP; SVG is forbidden in this wave because it can carry scripts and the local reader serves the images of proprietary packages as they are.
vibe checkand the publish gate verify existence, format signature, proportions and size — by signature and dimensions, never by file extension. The limits are small on purpose: packages of ordinary kinds are materialised and committed at consumers. iconshows in the package page header and on shelf cards;bannerheads the package page;previewservesog:image,twitter:imagewith thesummary_large_imagecard, andimagein JSON-LD.- Placeholders are generated, never stored: a gradient or pattern for the banner and a glyph for the icon are computed from the hash of the coordinate, so a package looks the same on the site and in the local reader and different packages are distinguishable; the glyph depends on the kind — a book for
doc, its own sign for every other kind; inline SVG at render time, no files, no network. - When
previewis absent, the site build composes one from the placeholder, the icon and the title — never by cropping the banner. One preview per package in the first wave; per-page cards are deferred. - A translation inherits the images of its source unless it declares its own.
- The site build copies images under hashed names for caching and never loads them from foreign addresses; the alt text is the
title.
47Decision: a card like arXiv's and like a social profile — title and abstract in the manifest, a separate preview, generated placeholders.
- 48Why: the index and the shelf must show the title and abstract without downloading the package; the banner's and the link preview's proportions are incompatible, so the preview is its own option; generated placeholders follow the law that derived things are never kept by hand.
- Considered and rejected: showing
descriptioninstead of a title; taking the title from the entry page; the abstract as a page section with a copy in the index; a static set of placeholders; a preview cropped from the banner; the field nameog_image(more than Open Graph reads the image); SVG in this wave. - Revisit when: a request for sanitised SVG icons; a request for multilingual titles inside one package — observed in BACKLOG.
8. Two levels of documentation
- 49Level 0. The site renders any published version of any package from its own bytes: the manifest as a reference page, the README, the boot snippet marked «read by the session», the specs with anchors and fact highlighting by address, the declared skills, binaries and MCP servers, the dependants, «explained in», «translated into», and the images of
[media]. Versions sit in the side navigation;latestis an alias. - The manifest of a level-zero rendering says so:
projection = true, so a shelf can tell a page the site derived from a package's own bytes from a page an author wrote, and mark the first as generated. A bridge's rendering carries the two authorships the bridge keeps apart. - Level 1. Extended documentation is a separate package of kind
doc, any number per subject. The site stitches the two levels on one package page: the level-0 overview above, the documentation shelves below. - Why: level 0 is free for the author and covers the whole registry at once, as rustdoc covers every crate; level 1 is needed only where somebody wants more, and forces nobody else to write anything.
- Considered and rejected: level 1 only — an empty site at the start; level 0 only — no room for tutorials and examples.
- Revisit when: never within this wave; the levels are orthogonal.
9. The site: addresses, sources, the host channel, hosting
9.1 Addresses
50The site is mounted under the path /doc of the main domain. The language is the path segment after /doc/; the source language of a documentation carries no prefix. The address map is deterministic and needs no index:
51spec://<group>/<name>@<version>/<document>#<anchor>
→ https://vibevm.org/doc/<group>/<name>/<version>/<document>/#<anchor>
spec://<group>/<name>/<document>#<anchor>
→ https://vibevm.org/doc/<group>/<name>/latest/<document>/#<anchor>
the ru translation of the same page
→ https://vibevm.org/doc/ru/<group>/<name>/<version>/<document>/#<anchor>
- 52A page address ends with a slash (
…/<document>/); the projections lie beside it as files (…/<document>.md,…/<document>.xml); an address without the slash receives a 308 redirect on the web and locally. The static adapter of the chosen stack generates correctly in this mode only (§12). - Pages of older versions carry
rel=canonicaltolatestwithin their language. The local reader uses the same scheme with another origin. - An address with a version number always shows the current content of that version in the registry: one number may be published ten times a day, and the site shows the last publication. There are no permanent links to past publications — they do not exist in the registry either (§14).
- Until the owner decides otherwise (ruling 2026-09-14), every package this repository publishes carries the number
1.0.0, and a change is published under the same number in place: the registry keeps no past publication, the site shows the current content of the number, and the changelog records what changed by date rather than by a new number. A consumer who pinned the number keeps the bytes the lock file recorded, and a fresh install gets the current ones (##SITE-VERSION-SHOWS-CURRENT, PROP-002##PUBLISH-MUTABLE-VERSIONS). - The whole domain is one site: the landing
vibevm.org/andvibevm.org/ru/and the documentation/doc/…are built by one build from one package. The root machine files —robots.txt,llms.txt,llms-full.txt,sitemap.xml,feed.xml, the IndexNow key file — are generated by the same build, keeping the addresses and content of the present landing:robots.txtis one per domain, ASCII-only, an allow-list of crawlers, and carriesSitemap:lines for/sitemap.xmland/doc/sitemap.xml; the rootllms.txtopens with the paragraph disambiguating the name «VibeVM» and links/doc/llms.txt; the documentation publishes its own files under/doc/:/doc/sitemap.xml,/doc/llms.txt,/doc/llms-full.txt,/doc/manifest.json. The rootllms-full.txtis the landing, not a copy of the documentation. - Why: a path under the main domain shares its authority for SEO; the same scheme on the web and locally means a link in the documentation works in both worlds; the language in the path, not in a parameter, is how it is indexed and cited; the landing is already indexed and carries the name disambiguation for AI crawlers, so its addresses and root files survive the move byte for byte where possible and are checked by the landing parity test.
- Considered and rejected: a subdomain
docs.vibevm.org; the language in a query parameter; the language in a subdomain; two sites on one domain — an Astro landing plus a Qwik documentation with a three-line contract between repositories. - Revisit when: never within the wave; changing the address scheme is a breaking change with redirects.
9.2 Sources and the host channel
53The site has two sources, both configured in the form of the project's [[registry]]:
- 54the package registry — one, by default the GitHub organisation
vibespecs; its index is a change feed: the site pollsrepomd.jsonandprimary.jsonl(or receives a webhook), compares the pairs «coordinate, content hash» with what is rendered, and rebuilds only what changed; - the host source repository — one, by default
github.com/vibevm/vibevm; from it the hostorg.vibevm.core/vibevmis rendered by the current state of the branchmain, and only by it.
- 55The host channel is a checkout on disk: the deploy runs
git checkout main && git pullin the renderer's directory, and the renderer reads the host from there as a project (level 0:README.md,vibevm/vibespecs/**, the boot snippet, the[project]manifest) and the core documentation as an in-tree package through the project-local registryvibevm/vibepacks. The host is not published to the registry for the sake of rendering, no git source of packages is used, andvibe cache addis not run on the server. The host has no release tags; the release is whatmainholds now. - The render cache is keyed by an internal content hash of the source (the store's recorded hash for store packages, a recomputation for the checkout), never by
(group, name, version): in-tree packages keep their version number while their content moves. The key is never shown to a reader (§14). - Rendering is idempotent and cached; a render error is shown as the version's page; reverse links — dependants, «explained in», «translated into» — come from the index and from the maps packages carry (
vibe specmap), never by hand. - The site polls the host branch and, when it changed, rebuilds the host render with a debounce, keeping one current render plus the previous one until the new one completes; no history of states is kept. The poll interval is a configuration value of the site build; the recommended default is once an hour (campaign atom A5.1, the measured render cost of the phase-0 spike A0.28 is the evidence), pending the owner's word.
- Mirrors and a second registry are the future; the owner opens them.
- Why: this is how docs.rs stands over the crates.io index, and cheaper here — a version is frozen by content hash. The host is not a registry package, and forcing it to become one for the site's sake would be a false generalisation; the owner's word is «the repository named in the settings, GitHub by default».
- Considered and rejected: a full rebuild on a schedule; publishing the host into the registry for the render; reading the host from a mirror; rendering by release tags (there are none); keeping host states by tree or commit hash (a feature that needs history — §14).
- Revisit when: the owner opens mirrors or a second registry.
9.3 Hosting and deploy
56The site lives on the same server and domain as today's landing, by the existing «bring up the site» runbook of the owner's private infrastructure document. The content of that document — addresses, ports, traffic scheme, VPN — is never transferred into this repository, its campaign zone or its commits; only the form of the decision is recorded here.
- 57Two containers instead of today's one. Serving: a stock
nginx:alpinewith a volume holding the rendered site for the whole domain —/,/ru/,/doc/…, the root machine files. Rendering:vibevm-site-renderer, an image in whichvibeis built from the currentmainand the shell is built; it fills the volume withvibe doc build-site(level 0 and the documentation from the registry and the host) and the static Qwik build. In the first wave the renderer runs from the deploy script after the pattern of today's landing script (git pullof the checkout →docker compose run --rm vibevm-site-renderer→docker compose up -d <serving service>); a timer or an index webhook is the second wave. - Until the new site is ready the domain is served by the present Astro landing from
vibevm-org. The cutover is one step: the serving container of the new site takes the place of the landing's container — the same compose service and the same port, so that the host nginx is not touched — after a green landing parity test and a local rehearsal of the full stack. Rollback is restoring the previous service. Neither cutover nor rollback touches the host nginx, the ports, the certificates, the neighbours' compose blocks or the VPN: that is the rule of the runbook itself and the boundary of the owner's VPN directive. - The site's container nginx inherits
vibevm-org/nginx.conf:charset utf-8withcharset_typesfor the text types;absolute_redirect off; port_in_redirect off;(TLS terminates outside);expires -1for HTML and an immutable cache for the hashed asset directories and/fonts/; the redirect/en/→/(301);X-Content-Type-Options nosniff;Referrer-Policy strict-origin-when-cross-origin;X-Frame-Options SAMEORIGIN;error_page 404;try_fileswith the directory index; andlocation = /q-manifest.json { return 404; }(§12). - Deploy is «a git checkout on the server plus
docker compose up -d --build»: the checkout is the vibevm repository (the renderer needsvibefrom source and the site package lives in its tree); the image is built on the server and pushed nowhere; the run is one command over SSH from the developer machine with the native Windows OpenSSH (the Git Bashsshswallows output). After a deploy:curl -sI https://vibevm.org/and/doc/answer 200, no redirect carriesLocation: http://,/llms.txtis served withcharset=utf-8, and IndexNow receives the changed URLs. - The renderer's
Dockerfileanddocker/nginx.confare in the packageorg.vibevm.doc/web(build configuration, not an artifact); the compose services, the deploy script, the checkout switch and the private document's entry are done on the server by the owner's hand or under his explicit supervision; invibevm-orgonly the final decommission commit lands after the cutover, by the owner's word. - DNS, TLS and the domain's ports already exist; nothing new is issued; the domain is not behind Cloudflare.
- Why: the owner's hosting exists, is documented and works; one site on the domain means one serving container and no proxy between containers; the renderer as a separate container because
vibe(Rust) and Qwik (Node) must not live in the serving image; taking the landing container's place with the same service and port is the only way to switch the domain without touching the host nginx the VPN depends on. - Considered and rejected: a separate domain or subdomain; third-party static hosting (domain authority, an existing infrastructure); edits to the host nginx; two sites on the domain with a
/doc/proxy between containers; building the image on the developer machine and pushing it to an image registry; thevibevm-orgcheckout as the site source after the cutover. - Revisit when: a move to another hosting or a CDN; a second server or a mirror.
9.4 The reader
58A documentation page (level 1; the README and specs of level 0 in the same reader) behaves as a reader for long texts. The normative part of that behaviour:
- 59Numbered blocks. Every flow block of the pivot — paragraph, list, table, fence, quote,
example,rule,note,figure,prompt; a heading keeps its own id and is not numbered (clarified 2026-09-12 at implementation, followingPIPE-NUMBERING) — receives an ordinal number and the idpNNat build time in the Rust pipeline (§10), never by a client script. The number is the block's position in the current text of the page, counted beforewhenfiltering, so thatp12names the same block in the build for any platform and agent and in every translation; gaps in the rendered output are accepted. The number appears in the HTML island, in the.mdand.xmlprojections (as[p12]at the start of the block) and inllms-full.txt, so a human and an agent cite one place. After an edit an old#p12may point at a neighbouring block, as a link to a file line does after an edit; this is accepted and nothing tries to «remember» it. Headings keep their named anchors{#id}above the number; named anchors are immutable and renamed only by tombstone, positionalpNNlive by the current text. - Switching the language keeps the place. The language selector leads to the same page in the other language with the same fragment (
#pNNor#id), which the block-for-block mirror of §5 makes possible. When the page does not exist in the chosen language, the site serves the source language under the chosen language's address as a statically materialised fallback (<html lang>of the source,rel=canonicalto the source page,noindex), shows a bilingual notice «this page is only in … so far» once per session, and rewrites internal links so navigation stays in the chosen language. The chosen language is remembered in the cookielangfor 365 days — the same name the landing uses, so the domain root and/doc/remember one choice. - Reading settings — theme (dark, light, system), font size, column width, anchors on or off — are kept in
localStorage(in embedded mode by the host throughpostMessage); the theme is applied before the stylesheet loads to avoid a flash. The default theme is the system's, with a manual switch. - Return to the reading place. The nearest anchor above the top of the viewport is remembered while scrolling; on the next visit the reader does not scroll by itself — a «return to place» button appears and disappears as soon as the reader scrolls past the first heading.
- The «for agent» button shows the
spec://…#pNNaddress of the current place, the links to the.md,.xmlandllms.txtof the package, and copies with one click. In embedded mode thepromptblock's «hand to agent» button sends the prompt text to the host (§11). - A documentation package MAY declare
[navigation]:pinned, the document paths the site and the local reader list first, in the order given; and[[navigation.section]], one row per top-level folder of the page tree with the title the navigation shows for it. Pinning changes only where the named pages stand; every other page keeps the manifest's order. A pinned path that names no page is an error ofvibe check. - The page's meta block shows the publisher, the package version and whether it is
latest, the publication date, for a translation the package it adapts, the audiences, the reading time, and the.md,.xmland «for agent» links. A print style prints without panels, with block numbers and with link addresses in footnotes. The page carries exactly two dates: when it was rendered and when it was last read aloud.
60The visual language (tokens, fonts, components) and the remaining reader details are design, not contract: they live in the vision (design/documentation-vision.xml, D-21 and D-22) as a working hypothesis until the design review after the first live render, and the normative floor for the shell is in §12.
9.5 Analytics
61Public documentation pages carry the same self-hosted first-party Umami tag as the landing — the same website id, moved from the landing's base layout into the site configuration; the paths /u/s.js and /u/e are served by the domain, the site does not touch them. Nothing beyond: no GA, no Metrika, no consent modal — Umami runs without cookies and personal data. The local reader and the embedded mode carry no tag.
- 62Considered and rejected: an own analytics instance; GA and Metrika; reading events (which paragraphs are read) — deferred.
- Revisit when: a request for events (search, copying an address for an agent).
10. The content pipeline: a Rust library and thin surfaces
63Everything with content lives in a Rust library, working name vibe-doc: the HTML backend of the pivot that produces a page's «island»; the JSON page manifest; llms.txt of every tier and language; the Markdown and XML projections; the checks of examples, citations, translations, coverage, media and style; placeholder and preview generation; the reading of sources (the store, the lock file, a registry, the checkout). The surfaces above it — the CLI vibe doc build | serve | check | manifest | todo | surface | diff | shell install, the MCP tools, the HTTP server — are projections of the library (PROP-000 §21).
- 64The Qwik shell parses nothing: it receives the island as finished HTML and its data by a JTD contract with generated TypeScript types. Every wire between Rust and TypeScript is a JTD schema registered in
formats/REGISTRY.toml; the types are generated bycargo xtask codegen(PROP-000 §16, PROP-044). Launch parameters reach the page as a non-executable block<script type="application/json" id="vibe-doc-config">, so the content security policy needs no'unsafe-inline'. - Crates:
vibe-doc(the library) →vibe-doc-shell(the embedded shell, §12) →vibe-doc-server(the local HTTP server, §11). The server repeats the form ofvibe-index(axum 0.8, one router builder, RFC 7807 errors,oneshottests) without importing its code; axum never entersvibe-doc,vibe-mcpor the site generator. Each new crate follows the campaign's new-crate checklist: workspace membership,#[spec]marks,specmap.tomlentry,conformgating, README, the self-check panel. - The
ruleelement is read on the host side: a host module scans documentation pages and injects into the traceability engine through its publicCodeScannerseam (build_with_scanner,check_with_scanner,write_with_scanner,CompositeScanner). The engine and its vendored copies are not edited andsync-enginesis not run. Documentation pages lie outsidespec_roots, the page-scan policy lives in a host file rather than inspecmap.toml, and the scanner is injected at both points ofxtask/src/specmap.rs— the main pass and the resolve gate — so the coverage gate cannot be green by emptiness. The edge's tail is aCodeItemwith the sentinelcrate_name = "<doc>", after the precedent of the JTD scanner's"<schema>"; the engine's wire schema does not change. - Block numbering is a pure function
number_blocks(&SpecDoc) -> Numberinginvibe-doc, called afterderivedblocks are expanded and beforewhenfiltering and the backends;BlockPathis the section path plus the block index; the backends ofvibe-doctake(doc, numbering)side by side; the pivot's ownto_xmlandto_markdowndo not change andNumberingnever enters the pivot. Every flow block is numbered; list items, table cells, the children ofpromptand headings are not. - The example runner (
vibe doc check --examples) runs everyexampleagainst the built binary and compares exactly after the declared normalisation; there are no match templates — every non-deterministic line is closed by a namedreplacerule visible in review. A page carriesrun,expect(stdout) and an optionalstderr; the attributeexitdefaults to0; an absentstderrasserts «stderr is empty». A fixture is a directory with a tree and anexample.tomldeclaring the normalisation rules and the map «--jsondocument → JTD schema» by thecommandfield. The command's working directory is a fresh sandbox with a copy of the fixture, never the source tree; the command names no paths; isolation is one variableVIBE_SETTINGSin the native path spelling plusNO_COLOR; the behavioural variables (VIBE_OFFLINE,VIBE_UNATTENDED,VIBE_INVOKED_BY,VIBETERM,VIBEFRAME) are cleared and the flags stand in the example itself; a tripwire checks that the real~/.vibeand the source tree stayed unchanged; stdout and stderr are captured separately; examples document the non-TTY branch of the product and interactive prompts are described in prose; a--jsonstream is parsed document by document and each is validated by the fixture's schema map with a JTD validator written invibe-doc(none exists invibe-wire, whose schemas are codegen input); a document without a schema is reported as unchecked, never as passed. Normalisation:<TMP>,<HOME>,<REPO>, slashes, CRLF in expected files,vibe <VERSION>(package versions untouched), ANSI, block sorting by a declared line form, the fixture's localreplacerules; the order is path replacements before slash unification and sorting after all replacements. Loosening a comparison to turn a check green is forbidden: the normalisation or the product is fixed. derivedblocks — CLI help, JTD schemas, manifest fields — are generated at build time from the product and are never committed as page text; a divergence is red except under an explicit--accept.expectis committed as golden output.- Why: the omnichannel law. If the site parsed Markdown anew, anchors and facts would lose their identity and the local and public renders would diverge.
- Considered and rejected: a static generator on the TypeScript side that reads the sources itself.
- Revisit when: never within the wave.
11. The local reader and the embedding contract
65vibe doc serve starts an HTTP server on 127.0.0.1 only, serves the shell and, on every request, glues in the island rendered from the machine store, the current project's lock file or a private registry. The language preference comes from the project's [i18n].preferred when present. The mode is fully autonomous: no request to vibevm.org, no external CDN or fonts, everything in the bundle. The server serves files only from known roots (the store, the shell), without path traversal and without directory listing, with a content security policy naming no external source.
- 66The policy is sent as a header, not a
<meta>:default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; connect-src 'self'; media-src 'self'; object-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors <origin>, whereframe-ancestorsnames only the origin of the host that launched the reader — the launch parametervibe doc serve --frame-ancestor <origin>, because a webview's origin changes from window to window — and is'none'without the parameter. The reader has no CORS layer at all and sendsx-content-type-options: nosniff. The public site is not embeddable in frames. - Shell statics are served through
ServeDirwithappend_index_html_on_directories(false)for a shell from the store, and by exact name match for the embedded shell; pages are served only by the server's own routes; hashed names getcache-control: immutable, pagesno-store. The formdata_dir.join(<capture>)of a percent-decoded path capture is never repeated in the reader. - The store is warmed with
vibe cache add <coordinate>from a registry, orvibe cache add --offline <coordinate>from a project root whose project-local registry holds the package in-tree; the sources ofvibe-docare the store (lookup,list_all), the lock file (Lockfile::read,slot_abs_path), a registry (resolve_and_fetch) and a checkout (LocalRegistry). - When the instance's shell pin names a shell that is not in the shell store and there is no network, the reader falls back to the bare shell and warns; it never contacts the network without consent (§12).
- The embedding contract for a webview: relative addresses and a base path set at launch; the host opens an address through the URL fragment or
postMessage{ "open": "spec://…" }; the reader sends{ "openFile": "<path>" }when a local link is clicked, so the host application opens the file in its editor; the theme is a launch parameter and{ "theme": "dark" | "light" }on the fly (a VS Code plugin follows the editor's theme); reading settings travel as{ "settings": {…} }in both directions; the language comes from[i18n].preferredor a parameter; the «hand to agent» button of apromptblock sends{ "prompt": "<text>" }outward and the host decides what to do with it; no external resources. - The local reader publishes nothing, contacts nothing, and carries no analytics tag.
- Why: this is how the documentation of proprietary packages is read; how a VS Code plugin gets the interface through an iframe; how content never leaves the machine and is never visible to other users of the machine.
- Considered and rejected: a separate «local application» with its own code; binding to
0.0.0.0. - Revisit when: the owner opens work on the VS Code plugin and wants a native interface instead of a webview.
12. The web stack and the shell
67The site is TypeScript under the typescript-ai-native discipline, on Qwik 2.0 beta (next.qwik.dev), pinned exactly together with the versions of Node and pnpm: @qwik.dev/core and @qwik.dev/router 2.0.0-beta.43 (the 2.0 line lives under the npm tag beta; latest is 1.x and is not used), Vite 8.2.1, Node 24.18.0, pnpm 10.33.2, lockfile v9, packageManager in package.json; the static adapter is ssg (@qwik.dev/router/adapters/ssg/vite). One shell code base, two adapters: static for the server (every route prerendered, the island glued at build time) and embedded for vibe.
- 68One site is built with
base: "/", and/,/ru/and/doc/…are route directories; the embedded adapter is a separate configuration withbase: "/doc/"and the documentation routes at the root, without the landing routes. - The static generator silently under-generates with exit code 0 (
trailingSlash: falseyields one page of seven; a wrongbaseyields none and an empty sitemap), so the site build gate counts pages — the generator'sGenerated: N pagesagainst the page manifest — and never trusts the exit code. q-manifest.jsonis removed from the build output and answered 404 by nginx; the starter'spublic/manifest.jsonis rewritten without a foreign$schema;pnpm-workspace.yamldeclaresallowBuildswithsharp: falseand@parcel/watcher: false; the base path and every path in the environment and arguments of build scripts are passed in a form MSYS does not rewrite (MSYS_NO_PATHCONV=1or values without a leading/) — a rule forDEV-GUIDE.md.- The package
org.vibevm.doc/webis one pnpm workspace of two parts:design/— tokens, themes, fonts and components;site/— the Qwik application with the landing routes (/,/ru/) and the documentation routes (/doc/…). Header, footer, theme, language selector and fonts are shared components, not copies. The package is published as source only; built assets never enter it (PROP-024 §2.2). - The web package passes the discipline's seven-step floor before every commit (
prettier → tsc → tests → eslint → conform → specmap → test-gate), invoked from the host root through the host's builttypescript-ai-nativebinary with--path; unit tests are.test.tswithout JSX; component behaviour is proven by the island parity test on the Rust side and by the build; thetsconfigis the full floor of the discipline guide plus the Qwik blocks (jsx,jsxImportSource,moduleResolution: "Bundler",paths), withexactOptionalPropertyTypeskept;any, uncheckedas,!and@ts-ignoreare forbidden in domain code; the resolution of the discipline's eslint plugin outside the repository layout is decided when the package is initialised. - The design floor of the shell: colours only through semantic tokens (
--bg,--text,--text-2,--line,--accent, …) — a colour literal in a component is a review error; two value maps (light, dark) in one palette file, switched byprefers-color-schemeand[data-theme]; both themes pass an APCA contrast audit as a floor step, with thresholds by role — body text Lc ≥ 75, secondary and tertiary text ≥ 60, interactive outlines ≥ 45; separators and the block numbers at reduced opacity are decorative and outside the gate; the APCA formula is the project's own implementation of the published algorithm (the reference library is AGPL and does not enter the tree); no Tailwind anywhere on the site; fonts self-hosted in the bundle as separate Latin and Cyrillic woff2 subsets withunicode-range, no external font service; motion only where meaningful and off underprefers-reduced-motion. - Node is needed only for the server build, in Docker after the landing's pattern: a build stage on a Node image builds the shell and renders the pages, the serving stage is stock nginx (§9.3). A reader's machine needs no Node.
69The embedding mechanism (proposed; the owner asked to see it before confirming — §1).
- 70A step
cargo xtask embed-doc-shellbuilds the web package from source (pnpm buildwith the embedded adapter) and places the result — the route template with the island slot, scripts, styles — in a directory the cratevibe-doc-shellincludes into the binary throughinclude_dir(0.7.4, MIT, four dependencies;rust-embedreads from disk in debug builds) behind the feature flagembedded-shell; abuild.rsdeclaresrerun-if-changed=shell, stops under the feature whenshell/index.htmlis missing with the recipe in the message, and exports the shell's digest as the compile-time constantVIBE_DOC_SHELL_SHA256. - A release build of
vibeenables the flag and fails when the shell is absent. A plaincargo buildwithout Node compiles with a fallback shell — bare HTML without scripts. - A build from source (
vibe self install, first-run) does not stay with the bare shell forever: on the firstvibe doc servethe reader offers to download the shell matching its version from the release assets;vibe doc shell install [--assume-yes]downloads it with consent after the pattern ofinstall, verifies the digest against the pin, and stores it in the shared content-addressed directory~/.vibe/opt/vibevm/doc-shell/<sha256>/— outside the immutable version instance, which carries aDOC-SHELL.lock.tomlwritten at staging. On refusal or offline the fallback shell works. Downloading happens only by explicit consent, never automatically. vibe doc serveserves the shell statics from the binary or from the shell store and glues the island rendered by the Rust pipeline into every page; oneShelltype with three sources and aprovenance()the command can report.- The shell's coordinate and digest are pinned beside
vibe; the self-check compares the embedded shell with the pin;vibe doc servecan say what it carries. - The shell is built with a configurable base path and relative addresses; it has no external scripts.
- A parity test runs both adapters over one package and compares the island byte for byte.
- In a release the shell is a separate asset
vibevm-doc-shell-<version>.zipwithout a platform target, with its own manifestDOC-SHELL.jsonbesideDISTRIBUTIONS.jsonand of the same form (schema_version,product,repository,version,tag,source_commit,asset{name, size, digest}), verified by the same digest check.DISTRIBUTIONS.jsongains no field: its schema is closed, and every installedvibe 1.0.0would loseself update. The release check that counts the platform assets is extended to the additional shell asset.
71Decision: Qwik 2.0 beta with exact pins; one island shared by the public and the local render; the shell embedded at release and downloadable with consent.
- 72Why: the owner chose Qwik 2.0, variant Б for the local reader and variant А for the server build; a beta is admissible under the project's law for a fresh but well-designed library, with an exact pin and a review trigger; the identity of the public and the local render rests on the shared island, not on a shared JavaScript runtime; the download step closes the hole where everyone building
vibefrom source would get a crippled reader. - Considered and rejected: Qwik SSR locally through an embedded JavaScript engine (QuickJS); the built shell beside the binary in the release zip instead of embedding; built assets inside the package
org.vibevm.doc/web; requiring Node to buildvibefrom source. - Revisit when: the stable Qwik 2.0 ships, or a breaking change of the beta demands a rewrite; the pin moves only in its own commit with the reason recorded in the campaign ledger.
13. The SEO and LLM-SEO contract
73The public site MUST:
- 74serve fully server-rendered HTML, with no content behind scripts;
- keep one canonical page per fact and language: older versions carry
rel=canonicaltolatestof their language; between languages,hreflangfor every available language andx-defaultfor the documentation's source language; - publish a sitemap index by packages and languages with
lastmodfrom the publication date, listinglatestonly; - explicitly allow the crawlers of OpenAI, Anthropic, Google, Perplexity and the others in
robots.txt; the list of agent names is verified against the providers' documentation at every build, never rewritten from memory, and the verification is recorded in the campaign ledger;robots.txtis one per domain, generated by the site build together with the rootllms.txt,sitemap.xmlandfeed.xml, kept ASCII-only as the earlier landing kept it; the domain is not behind Cloudflare, so no crawler block is to be lifted there; - serve every text format with
charset=utf-8and every redirect relative (absolute_redirect off; port_in_redirect off;) — TLS terminates outside the container, and an absoluteLocation: http://…sends strict fetchers into a redirect loop; - after every deploy that changed pages, send the list of changed URLs to IndexNow with the landing's key (the key file already exists at the domain root);
- carry JSON-LD (
TechArticlewithheadline,abstract,author,inLanguage,keywords,datePublished,image;SoftwareApplication;BreadcrumbList;FAQPagewhere apt), Open Graph and a Twitter Cardsummary_large_imagewith the preview from[media].previewor the composed card (§7); - publish
llms.txt(an index with one-line summaries) andllms-full.txtfor the base corpus, plusllms-small.txtandllms-medium.txtunder a token budget, all derived from the same manifest as the navigation, the full corpus ordered by the layer law (stable before mutable); the same files per language and per package; the registryllms.txtis a catalogue of documentations in the arXiv style — title, officiality star, publisher, language, audiences, abstract, link; - serve every page as clean Markdown at the address with the
.mdsuffix and as raw XML at the.xmlsuffix; - serve the JSON page manifest with officiality statuses, languages, audiences, genres, anchors and summaries (
/doc/manifest.json), a resolver endpoint forspec://addresses (/doc/resolve?uri=…), and optionally an MCP server of the site; - keep a dense internal link graph: dependants, «explained in», «translated into», specification ↔ documentation;
- build pages by one template: the answer in the first sentence, one concept per page, a glossary of canonical terms, questions written as questions, examples with expected output; scenario pages prompt first, mechanism second, manual steps only when they are needed (§16).
- 75The local mode publishes none of this and contacts nothing external.
- Why: the owner's word — maximal SEO including LLM SEO; every item is either a standard or a consequence of a project law.
- Considered and rejected: blocking AI crawlers; hand-written page descriptions; a link preview cropped from the banner; a separate
robots.txtunder/doc/. - Revisit when: a new standard of machine index appears.
14. Observed, checked, covering obligations — never judged
- 76
docpackages enter the include globs offacts.tomland the traceability map: the documentation is observed like every authored text of the project. - They are not judged: their facts do not enter the judging debt, because the genre is non-normative. The mechanism is the key
[judging] exempt = ["<glob>", …]infacts.toml— «observed, not judged» — read by the debt script now and by the shipped verb later (PROP-047##DEBT-MUST-BE-ASKABLE); the corpus offacts checkand the map do not change. Avibe checkcell verifies that the path of every in-tree package of kinddocis covered byexempt, so the list cannot go stale. - Every
ruleyields adocumentsedge without a pin: the citation is live, and the page shows the fact's current text by address at every render.vibe doc check --citationschecks one thing — that the anchor exists; a vanished anchor without a tombstone fails the build. There are no revisions, no text hashes and no «the spec moved ahead»: such checks would need a history the project does not keep by design. - Whether the prose around a citation went stale is a human's question at full reconciliation, not a machine's.
- Examples with
expectrun in the self-check panel as golden tests — the only technical link between the product and the documentation. Whether to keep it there or move it to a measurer is the owner's open question (vision §10, question 12); until his word it stays in the panel. - The coverage gate. Spec facts marked
actionstage="doc"with an audience are obligations;vibe doc check --coveragerequires every obligation to be cited by a page for the same audience — this is the ratchet PROP-047##DOC-COVERAGE-RATCHETnames, andvibe progress report --view doc --audience …is its listing of obligations, a source for the gate and not a navigation. Marking the obligations in the spec corpus is a campaign atom of its own. The site's navigation is derived from the page manifest, never from this report. - The audience vocabulary of PROP-043 gains
agent;user,authoranddevstay. Text markedaudience="agent"obeys the laws of agent text: a token budget, no narration, and never in a boot prefix — the site serves it in the «for agents» section and first inllms.txt. Audiences are not declared in the manifest; they are derived from the page markup. - A version is a behavioural contract, not a frozen set of files. Inside a version the product changes as often as it likes — amend, rewritten history,
vibe self update --force— and this is invisible by design. The only event by which the documentation computes «a difference between versions» is the owner's conscious bump of the version number: no checksums, no file hashes, no history. - The pseudo-history of versions is the documentation developers' kitchen: the documentation package keeps a surface snapshot of the product per declared version —
maintenance/surface/<version>.json: a structural description, not a hash — commands and flags from--help, manifest and lock-file fields, schemas, the text of the spec facts markedactionstage="doc", the format registry.vibe doc surface --record <version>writes it at a version bump and at the end of every full reconciliation;vibe doc diff <old> <new>compares two snapshots and, through the citation graph, thederivedsources and the coverage map, lists the pages to update, each with its reason. The samevibe doc diff <version> nowmay run inside a version as a hint to reconciliation — kitchen, never a fact for a reader. - Nothing of the kitchen reaches a reader: no «checked against», no «stale since», no drift counters, no fingerprints, no hash permalinks, no «a new version is available». The only dates on a page are when it was rendered and when it was last read aloud. A human changelog between versions is written by hand from the output of
vibe doc diff. - There are no locks. No technical gate binds a product release to the documentation:
vibe doc todoprints the gaps as a number; the checkbox «documentation: updated / debt recorded / not needed» in the pull-request template is a habit; only internal breakage of the documentation stays red — a red example, aderivedthat fails to build, a vanished citation anchor. Drift between full reconciliations is an accepted risk; the full reconciliation is a team promise on a calendar — once a quarter and before a major milestone — not a release condition. - The maintenance tools:
vibe doc todo(the queue by the current state of product and documentation — coverage gaps, red examples, unresolvable citations, page age, debt, linter statistics),vibe doc surfaceandvibe doc diff,reviews.toml(the last read-aloud date per page and the «page of the week» order), the packageCHANGELOG.mdwritten from the journal, and documentation debt asdocs:lines inBACKLOG.md. The maintenance regulation itself is derived from the campaign journal in phase 6 and becomes the next PROP; every rule of it cites a journal entry.
77Decision: observed and checked, never judged; coverage by obligations; versions as contracts; no release lock.
- 78Why: a one-way relation without reverse discovery rots silently; coverage measured by a gate answers «is everything told», which navigation does not; the sixth edition of the vision confused a contract with a frozen file set and tried to distinguish the indistinguishable — the owner's word restored the version number as the only event; the release lock was rejected by the owner on 2026-09-10 as untrue for a project that releases ten times a day.
- Considered and rejected: a hand-kept page list as the source of truth; a table of contents from the obligations report; judging documentation facts; checksums, tree and commit hashes, fingerprints as product identity; surface snapshots per change instead of per declared version; any state marks on pages for readers; permanent links to past publications; «new version» notifications; the technical gate «the product does not ship without documentation».
- Revisit when: the owner wants to publish a machine changelog between versions for readers — then the output of
vibe doc diffgets a human projection, still only by declared versions; a second project wants the same maintenance ritual for itsdocpackages — then the regulation becomes a flow package.
15. The fate of docs/
- 79
docs/moves todocs-legacy/in one move-only commit that changes nothing else — an archive, not a deletion, after the precedent oflegacy-spec/. - The new documentation is written against it: a claim that was there and is absent from the new documentation is either dropped with a recorded reason or a regression to fix. The inventory (
campaigns/docs-2026-09/LEGACY-INVENTORY.md: every claim of the old tree with its disposition) is closed before the documentation package ships — no empty rows. - Nothing in the living corpus cites
docs-legacy/as a normative source; it joins no include glob; it is history kept readable. README.md,DEV-GUIDE.mdandRUNTIME-GUIDE.mdstay in place and are updated by the same-commit law of PROP-000 §19.- Considered and rejected: deleting
docs/; rewriting it in place. - Revisit when: the regression list is closed and the owner confirms; then
docs-legacy/may be deleted in its own commit.
16. The style law of the documentation prose
80The style norm is the document AUTHORING.md of the package org.vibevm.core/vibevm-docs (during the campaign: campaigns/docs-2026-09/STYLE.md); this section states its binding facts.
- 81The source language is English.
org.vibevm.core/vibevm-docscarries[i18n].canonical = "en". Every other language, Russian included, is an adaptation published as its own package (§5): a mirror block for block, free sentence for sentence, with its own jokes and its own glossary of terms. Machine translation may produce a draft and never the text. - The reader is intelligent, busy and has read nothing of ours. A page MUST work as the only page the reader ever opens: terms introduced on the page, the explanation complete without the specs, the spec cited as confirmation and never as a deferral.
- Where complexity lives. Only in containers the reader sees coming:
ruleblocks, tables,derivedblocks, fences, the glossary. Narrative paragraphs are corridors: a term is introduced before use (a link to its glossary entry or a gloss in the same sentence), at most two glossary terms per narrative sentence, one new idea per paragraph named in its first sentence, «see the specification» in place of an explanation is forbidden, concepts ordered as a ladder where each rung uses only the rungs below it. - Technical passages follow ASD-STE100: one word, one meaning — technical nouns have no synonyms; one instruction per sentence, imperative, present tense, active voice; at most 20 words in a procedural sentence and 25 in a descriptive one, at most six sentences in a paragraph; a warning before the step; sequences as numbered lists.
- The register is an essay, not a manual, after the writers who explain mechanisms to intelligent readers for a living: mechanism first and in plain words, short words over long, the first sentence carries the news, concrete detail over abstraction, honesty about trade-offs.
- Humour is rare, dry and placed: at most one joke per page, never in a procedure, a warning, a reference table, an explanation of an error, a
ruleblock or a title; informative, understood by an educated person of any country without an IT background; replaced or dropped in an adaptation, never translated. - Model tics are deleted on sight. The banned words and phrases per language are data for the linter and live in the documentation package as
style/banned.en.txtandstyle/banned.ru.txt(source, not generated). Headings named Overview, Summary, Conclusion, Next steps or Key takeaways, exclamation marks, emoji, bold in prose and a closing summary sentence are errors. - The page skeleton. A concept page: a noun-phrase title; a first paragraph saying what this is and when it is needed with no glossary term at all (it is also the page's line in
llms.txt; clarified 2026-09-12 at the first corpus check: a glossary word used in its ordinary English sense — package, project, kind, feature, workspace, translation — does not count, a word that means something only in vibe — lock file, manifest, registry, store, index, anchor, skill, contribution, fingerprint, receipt — does); anexamplewith itsexpect; the mechanism as a ladder; edge cases throughrule; questions only if real. A task page: an imperative title; the first paragraph without terms; then theprompt(§16##STYLE-PROMPT-FIRST); «what happens» in three to six sentences as a corridor; «by hand» as numbered STE steps only where the manual road is worth walking; edge cases and questions. No page ends with a conclusion, a summary or a «next steps» box. - Prompt first. Any action in VibeVM is done by an agent or by hand, and the agent is the main road. A scenario page opens with a
promptblock: the plain request in the user's voice, self-contained (coordinates, paths and registry named, never implied), one prompt for one outcome, free of secrets, neutral to the agent (it works for any agent with thevibevmskill), at most sixty words; beside itneeds(what the agent must have) andoutcome(what the person sees when it worked), and at least oneassert— a shell command that must exit zero after the agent's work, because a prompt cannot be checked by its output the way a shell example can. A prompt without an assert on a scenario page is a style-linter error; an illustrative prompt on an explanation page is markedassert="none".vibe doc check --promptsruns every prompt through the configured agent runner ([doc.prompts] runner) in a clean temporary directory with the fixture and then runs the asserts; it is not part of the panel — it runs in the prose phase before acceptance, as a sample in the monthly loop, and in full at reconciliation. The skillvibevm-docstakes a page'spromptas the task when the user asks for what the page describes. - Who writes. Prose — pages, first paragraphs,
titleandabstract, the glossary, questions, the skill text, adaptations — is written by the strongest model in the central session (the owner named Fable, Astra and Sol), never by a delegated worker; a worker's draft of prose is rewritten, not edited. Workers do the work around the prose: fixtures, expected output, the block mirrors of adaptations, code, checks, the shell. - The mechanical check
vibe doc check --stylegates a page: banned words and phrases by the page's language; sentence and paragraph length by block kind (errors in procedures and warnings, warnings in corridors); a glossary term used before its introduction; more than two glossary terms in one narrative sentence; deferral phrases («see the specification», «as described in») without an explanation on the same page; forbidden headings; exclamation marks, emoji, bold in prose; a readability score per page reported and not gated. A false positive is fixed in the linter's rule with a BACKLOG entry, never worked around in the text. A page is committed only after the author's self-edit against the norm and a green--style. - The human check: the owner reads three pages aloud at the phase gate — the newcomer's route, a reference page, an architecture explanation; a page that cannot be read aloud without stumbling goes back.
- Why: the owner's word — model prose suffers tics, but the greater harm is misplaced density: an agent writes as if the reader had read every spec; STE gives checkable rules for the technical places, the essay register for the rest, and the container/corridor split makes «where it is hard» predictable and checkable; English as the source is how the project is built and how AI crawlers read; adaptation rather than translation because jokes and rhythm do not translate; prose is the most expensive and most visible part of the work and the one worth the strongest model, since a gate can check code and fixtures but not text.
- Considered and rejected: Russian as the source language; literal translation; delegating prose to cheaper models for speed; pure infostyle for Russian (too dry — only its war on clutter is taken); developers' in-jokes; the Thing Explainer extreme (STE applies to technical places, not to the essay); AI «smoothing» of finished pages.
- Revisit when: the owner's style review at the phase gate yields remarks; a human editor appears; the linter starts to hinder more than help — two false positives in a row on one rule are a BACKLOG entry.
Invariants
82(The load-bearing rules of this PROP in one place. If anything below seems violated in practice, stop and reconcile before proceeding.)
- 83Documentation cites an anchor and never restates a normative value — a number, a flag, a path, a field name. Where the prose must mention a value, the same sentence carries the source address.
- No documentation page enters
STATIC.xml,INDEX.mdor a boot snippet. Adocpackage has no[boot_snippet]; agent-audience text is never in a boot prefix. - The relation is one-way in the source and two-way in the render. A spec's text knows nothing of documentation; «explained in», «translated into» and «depends on» are computed from the index and the maps and never written by hand.
- Officiality is computed from the convergence of edges at every render and stored nowhere as a flag.
- Derived things are generated, never kept by hand: the command reference, field tables, navigation, page manifests,
llms*.txt, placeholders, previews, reverse links, block numbers. - Every example runs. An
examplewithoutexpectis not an example; a comparison is never loosened to turn green. - Anchors are immutable; a rename is a tombstone — for published documentation pages as for specs, and for translations, which must match their source.
- A translation mirrors the source tree file for file and block for block and authors no examples.
- A version is a behavioural contract. Nothing in the documentation machinery depends on real history, checksums or tree hashes; the only version event is the owner's bump; readers never see the kitchen.
- No technical gate binds a product release to the documentation. Drift between reconciliations is accepted; only internal breakage of the documentation is red.
- The local reader listens on 127.0.0.1 only, serves only known roots, loads nothing external, and contacts the network only for a shell download the user explicitly confirmed.
- A package carries source only: no build artifacts in the web package, images as source files within the card limits.
- Content logic lives in the Rust library; every surface — CLI, MCP, HTTP, the shell — is a thin projection over a JTD contract.
- Prose is written by the strongest model in the central session and never by a worker; English is the source and every other language an adaptation.
- Nothing from the private infrastructure document enters the repository, and nothing in the campaign touches the host nginx, ports, certificates, neighbouring services or the VPN.