VibeVM
Contents
On this page
en
Publisher
org.vibevm.core
Version
1.0.0latest
Audiences
user, author, agent
Reading time
52 min
Rendered
Read aloud
never

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 doc package MUST declare at least one subject in [[documents]] (§4) and MUST carry title and abstract (§7).
  • A doc package MUST NOT declare [boot_snippet], [[mcp_server]] or [[binary]]; vibe check rejects the manifest. Documentation never enters a boot lane (§14).
  • A doc package 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 install MUST refuse a doc package with a hint naming the warm-up command; documentation is warmed into the machine store with vibe cache add (§11), and the local reader and vibe explain read 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 of vibedeps/ keeps consumer trees thin and keeps tutorials out of an agent's grep over 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 flow and could not forbid a boot snippet; materialising doc packages into vibedeps/ by default — it bloats every consumer's committed tree; a fifth layout root vibevm/vibedocs/ — it breaks the one-module layout law of PROP-052 for no gain, since addressing, scanners and the pivot already work over vibevm/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 install refusal 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 tool is mechanical: a tool lives in a project and runs through vibe bin exec by the lock file; an app runs 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 app with 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 is org.vibevm.core/vibevm-docs, the companion of the host coordinate org.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>-docs as 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 -lang and -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 -docs in 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 a doc package, may list several subjects, and its version is a semver constraint.
  • [documentation] in the subject names coordinates without versions; primary names at most one package, official any 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>-docs in 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 doc package with vibe cache add warms its subjects too, so that spec:// citations resolve offline.
  • The relation fields enter the registry index entry (title, abstract, documents, documentation, translates, media; the version_entry vocabulary of formats/vocabularies.json widens through cargo xtask codegen), so that reverse links are built from primary.jsonl and 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-doc by one fold over primary.jsonl in 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/vibevm of the core documentation is a project coordinate (the host root is [project], not a registry package — design/host-as-package.xml); vibe check verifies 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 = true in 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]]; title and abstract sit inside [package] beside description. 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 documentation field of Cargo.toml.
  • Considered and rejected: a capability docs:<coordinate> in provides — the capability grammar admits only kebab-case in both halves (crates/vibe-core/src/capability_ref.rs) and a coordinate does not fit; an ordinary requires dependency 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 documents and translates — 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 doc package is the existing [i18n].canonical of PROP-003 §2.7 (default en); there is no separate lang field. [i18n].available of a doc package 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 translates edges at every render — exactly as officiality from documents and documentation.
  • The sidecar layout of PROP-003 §2.7.1 does not apply to doc packages; the BCP-47 tags, the preference chain (I18nDecl, project_preference_chain, resolve_localised) and the lock file's [meta].language_chain are 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 --translations verifies 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 from reviews.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 documents of a translation MUST equal the documents of its source; vibe check verifies it.
  • An official translation is one that declared translates on 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; hreflang and x-default are emitted; llms.txt exists per language.
  • The local reader serves the same from the store; the preference comes from the project's [i18n].preferred or a launch flag.
  • Normative specifications stay in the specification's language: a rule shows 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.

40
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.txt marks official items so that an agent prefers them but sees the rest; the index carries the relation fields, the language, title and abstract.
  • 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
  • 46title is 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.
  • description stays the one-line subtitle for lists and the page's meta description; abstract answers 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 through derived kind="manifest-field".
  • Audiences and languages are not declared on the card: they are derived from the page markup and the translates edges.
  • A doc package MAY declare authorship in [package]: human, ai or mixed — 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 is spec://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 check and 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.
  • icon shows in the package page header and on shelf cards; banner heads the package page; preview serves og:image, twitter:image with the summary_large_image card, and image in 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 preview is 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 description instead 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 name og_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; latest is 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=canonical to latest within 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/ and vibevm.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.txt is one per domain, ASCII-only, an allow-list of crawlers, and carries Sitemap: lines for /sitemap.xml and /doc/sitemap.xml; the root llms.txt opens 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 root llms-full.txt is 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 polls repomd.json and primary.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 host org.vibevm.core/vibevm is rendered by the current state of the branch main, and only by it.
  • 55The host channel is a checkout on disk: the deploy runs git checkout main && git pull in 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 registry vibevm/vibepacks. The host is not published to the registry for the sake of rendering, no git source of packages is used, and vibe cache add is not run on the server. The host has no release tags; the release is what main holds 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:alpine with a volume holding the rendered site for the whole domain — /, /ru/, /doc/…, the root machine files. Rendering: vibevm-site-renderer, an image in which vibe is built from the current main and the shell is built; it fills the volume with vibe 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 pull of the checkout → docker compose run --rm vibevm-site-rendererdocker 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-8 with charset_types for the text types; absolute_redirect off; port_in_redirect off; (TLS terminates outside); expires -1 for 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_files with the directory index; and location = /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 needs vibe from 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 Bash ssh swallows output). After a deploy: curl -sI https://vibevm.org/ and /doc/ answer 200, no redirect carries Location: http://, /llms.txt is served with charset=utf-8, and IndexNow receives the changed URLs.
  • The renderer's Dockerfile and docker/nginx.conf are in the package org.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; in vibevm-org only 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; the vibevm-org checkout 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:

  1. 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, following PIPE-NUMBERING) — receives an ordinal number and the id pNN at 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 before when filtering, so that p12 names 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 .md and .xml projections (as [p12] at the start of the block) and in llms-full.txt, so a human and an agent cite one place. After an edit an old #p12 may 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, positional pNN live by the current text.
  2. Switching the language keeps the place. The language selector leads to the same page in the other language with the same fragment (#pNN or #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=canonical to 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 cookie lang for 365 days — the same name the landing uses, so the domain root and /doc/ remember one choice.
  3. Reading settings — theme (dark, light, system), font size, column width, anchors on or off — are kept in localStorage (in embedded mode by the host through postMessage); the theme is applied before the stylesheet loads to avoid a flash. The default theme is the system's, with a manual switch.
  4. 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.
  5. The «for agent» button shows the spec://…#pNN address of the current place, the links to the .md, .xml and llms.txt of the package, and copies with one click. In embedded mode the prompt block's «hand to agent» button sends the prompt text to the host (§11).
  6. 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 of vibe check.
  7. 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, .xml and «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 by cargo 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 of vibe-index (axum 0.8, one router builder, RFC 7807 errors, oneshot tests) without importing its code; axum never enters vibe-doc, vibe-mcp or the site generator. Each new crate follows the campaign's new-crate checklist: workspace membership, #[spec] marks, specmap.toml entry, conform gating, README, the self-check panel.
  • The rule element is read on the host side: a host module scans documentation pages and injects into the traceability engine through its public CodeScanner seam (build_with_scanner, check_with_scanner, write_with_scanner, CompositeScanner). The engine and its vendored copies are not edited and sync-engines is not run. Documentation pages lie outside spec_roots, the page-scan policy lives in a host file rather than in specmap.toml, and the scanner is injected at both points of xtask/src/specmap.rs — the main pass and the resolve gate — so the coverage gate cannot be green by emptiness. The edge's tail is a CodeItem with the sentinel crate_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) -> Numbering in vibe-doc, called after derived blocks are expanded and before when filtering and the backends; BlockPath is the section path plus the block index; the backends of vibe-doc take (doc, numbering) side by side; the pivot's own to_xml and to_markdown do not change and Numbering never enters the pivot. Every flow block is numbered; list items, table cells, the children of prompt and headings are not.
  • The example runner (vibe doc check --examples) runs every example against the built binary and compares exactly after the declared normalisation; there are no match templates — every non-deterministic line is closed by a named replace rule visible in review. A page carries run, expect (stdout) and an optional stderr; the attribute exit defaults to 0; an absent stderr asserts «stderr is empty». A fixture is a directory with a tree and an example.toml declaring the normalisation rules and the map «--json document → JTD schema» by the command field. 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 variable VIBE_SETTINGS in the native path spelling plus NO_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 ~/.vibe and 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 --json stream is parsed document by document and each is validated by the fixture's schema map with a JTD validator written in vibe-doc (none exists in vibe-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 local replace rules; 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.
  • derived blocks — 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. expect is 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>, where frame-ancestors names only the origin of the host that launched the reader — the launch parameter vibe 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 sends x-content-type-options: nosniff. The public site is not embeddable in frames.
  • Shell statics are served through ServeDir with append_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 get cache-control: immutable, pages no-store. The form data_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, or vibe cache add --offline <coordinate> from a project root whose project-local registry holds the package in-tree; the sources of vibe-doc are 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].preferred or a parameter; the «hand to agent» button of a prompt block 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 with base: "/doc/" and the documentation routes at the root, without the landing routes.
  • The static generator silently under-generates with exit code 0 (trailingSlash: false yields one page of seven; a wrong base yields none and an empty sitemap), so the site build gate counts pages — the generator's Generated: N pages against the page manifest — and never trusts the exit code.
  • q-manifest.json is removed from the build output and answered 404 by nginx; the starter's public/manifest.json is rewritten without a foreign $schema; pnpm-workspace.yaml declares allowBuilds with sharp: false and @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=1 or values without a leading /) — a rule for DEV-GUIDE.md.
  • The package org.vibevm.doc/web is 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 built typescript-ai-native binary with --path; unit tests are .test.ts without JSX; component behaviour is proven by the island parity test on the Rust side and by the build; the tsconfig is the full floor of the discipline guide plus the Qwik blocks (jsx, jsxImportSource, moduleResolution: "Bundler", paths), with exactOptionalPropertyTypes kept; any, unchecked as, ! and @ts-ignore are 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 by prefers-color-scheme and [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 with unicode-range, no external font service; motion only where meaningful and off under prefers-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).

  1. 70A step cargo xtask embed-doc-shell builds the web package from source (pnpm build with the embedded adapter) and places the result — the route template with the island slot, scripts, styles — in a directory the crate vibe-doc-shell includes into the binary through include_dir (0.7.4, MIT, four dependencies; rust-embed reads from disk in debug builds) behind the feature flag embedded-shell; a build.rs declares rerun-if-changed=shell, stops under the feature when shell/index.html is missing with the recipe in the message, and exports the shell's digest as the compile-time constant VIBE_DOC_SHELL_SHA256.
  2. A release build of vibe enables the flag and fails when the shell is absent. A plain cargo build without Node compiles with a fallback shell — bare HTML without scripts.
  3. A build from source (vibe self install, first-run) does not stay with the bare shell forever: on the first vibe doc serve the 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 of install, 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 a DOC-SHELL.lock.toml written at staging. On refusal or offline the fallback shell works. Downloading happens only by explicit consent, never automatically.
  4. vibe doc serve serves the shell statics from the binary or from the shell store and glues the island rendered by the Rust pipeline into every page; one Shell type with three sources and a provenance() the command can report.
  5. The shell's coordinate and digest are pinned beside vibe; the self-check compares the embedded shell with the pin; vibe doc serve can say what it carries.
  6. The shell is built with a configurable base path and relative addresses; it has no external scripts.
  7. A parity test runs both adapters over one package and compares the island byte for byte.
  8. In a release the shell is a separate asset vibevm-doc-shell-<version>.zip without a platform target, with its own manifest DOC-SHELL.json beside DISTRIBUTIONS.json and of the same form (schema_version, product, repository, version, tag, source_commit, asset{name, size, digest}), verified by the same digest check. DISTRIBUTIONS.json gains no field: its schema is closed, and every installed vibe 1.0.0 would lose self 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 vibe from 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 build vibe from 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=canonical to latest of their language; between languages, hreflang for every available language and x-default for the documentation's source language;
  • publish a sitemap index by packages and languages with lastmod from the publication date, listing latest only;
  • 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.txt is one per domain, generated by the site build together with the root llms.txt, sitemap.xml and feed.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-8 and every redirect relative (absolute_redirect off; port_in_redirect off;) — TLS terminates outside the container, and an absolute Location: 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 (TechArticle with headline, abstract, author, inLanguage, keywords, datePublished, image; SoftwareApplication; BreadcrumbList; FAQPage where apt), Open Graph and a Twitter Card summary_large_image with the preview from [media].preview or the composed card (§7);
  • publish llms.txt (an index with one-line summaries) and llms-full.txt for the base corpus, plus llms-small.txt and llms-medium.txt under 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 registry llms.txt is 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 .md suffix and as raw XML at the .xml suffix;
  • serve the JSON page manifest with officiality statuses, languages, audiences, genres, anchors and summaries (/doc/manifest.json), a resolver endpoint for spec:// 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.txt under /doc/.
  • Revisit when: a new standard of machine index appears.

14. Observed, checked, covering obligations — never judged

  • 76doc packages enter the include globs of facts.toml and 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>", …] in facts.toml — «observed, not judged» — read by the debt script now and by the shipped verb later (PROP-047 ##DEBT-MUST-BE-ASKABLE); the corpus of facts check and the map do not change. A vibe check cell verifies that the path of every in-tree package of kind doc is covered by exempt, so the list cannot go stale.
  • Every rule yields a documents edge without a pin: the citation is live, and the page shows the fact's current text by address at every render. vibe doc check --citations checks 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 expect run 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 --coverage requires every obligation to be cited by a page for the same audience — this is the ratchet PROP-047 ##DOC-COVERAGE-RATCHET names, and vibe 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, author and dev stay. Text marked audience="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 in llms.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 marked actionstage="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, the derived sources and the coverage map, lists the pages to update, each with its reason. The same vibe doc diff <version> now may 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 todo prints 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, a derived that 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 surface and vibe doc diff, reviews.toml (the last read-aloud date per page and the «page of the week» order), the package CHANGELOG.md written from the journal, and documentation debt as docs: lines in BACKLOG.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 diff gets a human projection, still only by declared versions; a second project wants the same maintenance ritual for its doc packages — then the regulation becomes a flow package.

15. The fate of docs/

  • 79docs/ moves to docs-legacy/ in one move-only commit that changes nothing else — an archive, not a deletion, after the precedent of legacy-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.md and RUNTIME-GUIDE.md stay 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-docs carries [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: rule blocks, tables, derived blocks, 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 rule block 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.txt and style/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); an example with its expect; the mechanism as a ladder; edge cases through rule; questions only if real. A task page: an imperative title; the first paragraph without terms; then the prompt (§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 prompt block: 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 the vibevm skill), at most sixty words; beside it needs (what the agent must have) and outcome (what the person sees when it worked), and at least one assert — 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 marked assert="none". vibe doc check --prompts runs 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 skill vibevm-docs takes a page's prompt as the task when the user asks for what the page describes.
  • Who writes. Prose — pages, first paragraphs, title and abstract, 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 --style gates 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.)

  1. 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.
  2. No documentation page enters STATIC.xml, INDEX.md or a boot snippet. A doc package has no [boot_snippet]; agent-audience text is never in a boot prefix.
  3. 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.
  4. Officiality is computed from the convergence of edges at every render and stored nowhere as a flag.
  5. Derived things are generated, never kept by hand: the command reference, field tables, navigation, page manifests, llms*.txt, placeholders, previews, reverse links, block numbers.
  6. Every example runs. An example without expect is not an example; a comparison is never loosened to turn green.
  7. Anchors are immutable; a rename is a tombstone — for published documentation pages as for specs, and for translations, which must match their source.
  8. A translation mirrors the source tree file for file and block for block and authors no examples.
  9. 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.
  10. No technical gate binds a product release to the documentation. Drift between reconciliations is accepted; only internal breakage of the documentation is red.
  11. 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.
  12. A package carries source only: no build artifacts in the web package, images as source files within the card limits.
  13. Content logic lives in the Rust library; every surface — CLI, MCP, HTTP, the shell — is a thin projection over a JTD contract.
  14. 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.
  15. 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.

For an agent

This page has a machine mirror. The citation carries the version rather than latest, so what an agent quotes does not move under it.

spec://org.vibevm.core/vibevm@1.0.0/common/PROP-057-documentation-packages-and-site

.md.xmlllms.txt