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

Write specs an agent can cite

01The text in a package is only useful to an agent if every rule in it has an address. This page shows the shape of such text: named sections, one idea per unit, a status on each, and an address that never changes once published.

Why an address

02A person and an agent share one repository and nothing else: no hallway, no shared memory, no tone of voice. The specification tree is the only channel between them, and a channel works when a message can be pointed at. «Fix the retry count» sends the agent guessing; «fix spec://org.acme/notes-flow/flows/notes/PROTOCOL#RETRY-COUNT» sends it to one line. The second costs about twenty tokens; the first costs hundreds and may land on the wrong rule.

03 spec://org.vibevm.world/addressable-specs/flows/addressable-specs/ADDRESSABLE-SPECS-PROTOCOL#THE-SPEC-TREE-IS-THE-ONLY-CHANNEL
04 spec://org.vibevm.world/addressable-specs/flows/addressable-specs/ADDRESSABLE-SPECS-PROTOCOL#FOR-POINT-CORRECTIONS-THE-URI-WINS

The address

05spec://<group>/<name>[@<version>]/<path>/<document>#<anchor>: the package coordinate, an optional version, the document's path inside vibevm/vibespecs/ without its extension, and an anchor. The version is a feature, never an obligation: absent, the address resolves against the freshest installed version. Anchors are section ids and fact ids in one address space, so a rule is cited the same way whether it is a section or one sentence.

06 spec://org.vibevm.world/addressable-specs/flows/addressable-specs/ADDRESSABLE-SPECS-PROTOCOL#URI-SCHEME-IS-THE-FULL-GRAMMAR
07 @<version> is optional — a feature, never an obligation (owner-ruled 2026-08-04, B-028: «указание версий — опциональная фича; если версия не указана — используется самая свежая»); absent, the address resolves against the freshest installed version of the package (semver-newest among the materialised vibedeps/ slots — the one deterministic offline reading of «самая свежая»). Several coexisting slots are therefore not an ambiguity but an ordered set with a defined maximum; an explicit @<version> still picks its exact slot, including a non-newest one.
08
vibe explain "spec://org.vibevm.core/vibevm/common/PROP-000#KIND-SET"
spec unit spec://org.vibevm.core/vibevm/common/PROP-000#KIND-SET
  unmarked — `kind ∈ {flow, feat, stack, tool, mcp, lang, doc, app}` — eight kinds; `mcp` shipped with [PROP-027](../modules/vibe-mcp/PROP-027-mcp-packages.xml); `doc` and `app` admitted by [PROP-057](PROP-057-documentation-packages-and-site.xml) through the `VIBEVM-SPEC.md` §4.1 amendment of 2026-09-12 (pending the owner's ratification at the merge of the docs-2026-09 branch; the code learns the two kinds in that campaign's phase 2). (§Invariants `INV-VOCABULARY` in this file carries the same list.) <status stage="spec" state="done" action="continue" actionstage="impl"/> (vibevm/vibespecs/common/PROP-000.xml:75)
  hash sha256:1effee7da317e236ec9b0745d0e02f922cd93134fd731a758428dce62db2a81f
  edges in:
    documents ← `authoring::write-a-lang-package` (vibevm/vibepacks/org.vibevm.core/vibevm-docs/v1.0.0/vibevm/vibespecs/authoring/write-a-lang-package.xml:14)
    documents ← `glossary::index` (vibevm/vibepacks/org.vibevm.core/vibevm-docs/v1.0.0/vibevm/vibespecs/glossary/index.xml:107)
    documents ← `model::packages-and-kinds` (vibevm/vibepacks/org.vibevm.core/vibevm-docs/v1.0.0/vibevm/vibespecs/model/packages-and-kinds.xml:45)

The unit

09A unit is one anchored heading and the text under it, up to the next heading; it carries one decision or one rule, makes sense on its own, and fits on a page. If a unit needs «and also», it is two units. Contract statements use MUST, SHOULD and MAY; a reader must never guess whether a sentence binds. Checkable claims stand outside code fences, because a fence carries no anchor and an instruction inside one is unverified by construction.

10 spec://org.vibevm.world/addressable-specs/flows/addressable-specs/authoring-rules#ONE-UNIT-CARRIES-ONE-DECISION
11 spec://org.vibevm.world/addressable-specs/flows/addressable-specs/authoring-rules#CONTRACT-STATEMENTS-USE-RFC-2119-VERBS
12 spec://org.vibevm.world/addressable-specs/flows/addressable-specs/authoring-rules#A-CHECKABLE-CLAIM-BELONGS-OUTSIDE-THE-FENCE

Two serialisations, one model

13A specification is written in Markdown or in the project's XML dialect, and both parse into one document model. In XML a section is an element named after its anchor, <retry-policy title="3. Retries">. A rule is an element named after its id, with the attribute fact="true" and a status. In Markdown the same rule is a paragraph opening with @fact:RETRY-COUNT and closing with @status:spec/done. A tool that knows nothing of your vocabulary still finds every rule by one attribute test, and a converter turns one form into the other, reporting anything a round trip would not preserve.

14 Decision (ADR-part; owner ruling 2026-08-22, verbatim): «сконвертируй и факты тоже. Предлагаю такой формат <fact-name fact="true" ...>. Таким образом кастомный XML-парсер всегда может найти соответствующие элементы». A fact serialises with its ID as the element name, carrying the DISCRIMINATOR attribute — <THE-LAW fact="true" status="impl/done">body</THE-LAW> — so a reader that knows nothing of the vocabulary still finds every fact by one attribute test. The recognition law: an element IS a fact iff its name is fact (the generic form, which stays in the dialect) or it carries fact="true". The named form is emitted whenever the id passes the same elementability predicate sections use (fact-id grammar already forbids leading digits, so the fallback tail is vocabulary collisions only); the typed-fact fence binding stays by id and does not change. The owner's second clause binds the scanners: the progress machinery must work when a fact's SOURCE — not a materialised copy — is authored XML; the host lane holds by construction (XML sources enter progress through the canonical MD projection) and is PINNED by explicit tests (an observed .xml source scans unit-for-unit equal to its MD twin), while the specmap engine's native reader learns the named form mirror-wise. The converter recipe bumps again (specdoc/2specdoc/3); the host re-materialises once, after both shapes land. The owner's third clause (2026-08-22, same sitting) binds the boot lanes: «статические и динамические лоадеры должны хорошо работать с новым синтаксисом фактов» — pinned at the transition's landing by (a) the static-splice determinism test running over a NAMED-shape snippet whose projected facts survive into STATIC, (b) the vibe-spec normal-closure byte-equality test running over BOTH serialisations (generic and named) of one dependency, and (c) the polygon re-run at specdoc/3, whose control package auto-adopts the named shape through to_xml — INDEX targets, STATIC splice and every machine loader then exercise the final syntax end-to-end; the agent half of the dynamic router is §5a's measurement, deliberately run AFTER this transition so it measures the shape that ships. Landed: the recipe is specdoc/3, the host's 37 slots re-materialised once with named facts live (the redbook README golden pins 45), the recognition law holds in both readers with fact="false" a loud error, progress holds full ParsedDoc parity between an XML source and its hand-pinned MD twin across two scans, and pins (a)–(c) are in the tree — the splice snippet ships <BOOT-RULE fact="true">, the normal closure compiles three lanes byte-equal, the polygon re-ran 3/3. A live lesson worth its line: XML reserves every case-insensitive xml-prefixed name, so XMLBOOT cannot be an element — the predicate refuses it and the generic form carries such ids.
15 Decision (ADR-part). The XML dialect is deliberately ISOMORPHIC to the Markdown-expressible structure — exactly the constructs the markup contract names, in XML syntax, and nothing more. A schema-foreign element or attribute is a loud parse error, never a silent skip (the same closed-vocabulary law the typed-fact grammar took). This is what makes the owner's degradation law hold by construction: XML→MD loses nothing semantic because the dialect cannot express what MD cannot; «всё невыразимое — не поддерживается» is enforced by the schema, not by a lossy converter. Reopened once, 2026-09-11, for exactly one genre — §7 ##DOC-VOCAB-REOPENING: the documentation vocabulary of PROP-057 is additive, gated by the package kind doc, and one-way to Markdown by law; for the spec vocabulary this decision stands unchanged.
16 Decision (ADR-part; owner ruling 2026-08-22, verbatim): «Гораздо логичней <three-bands title=\"…\">. … Вся суть XML нотации в том, что у тебя названия тэгов несут названия сущностей, это упрощает работу нейросети». A section serialises with its ANCHOR as the element name — <three-bands title="1. The three bands"> — because the dialect's first reader is an agent, and a tag that names its entity is self-describing where an endless <section> river is not. The generic form <section id="…" title="…"> remains in the dialect as the REQUIRED fallback for the two cases XML itself forbids or the grammar reserves: an anchor that is not a valid XML name (leading digit) and an anchor colliding with the structural vocabulary (spec,title,status, section,p,fact,list,item,table,tr,td,fence,quote») — measured over the live corpus, that tail is 2 anchors of 1393; the emitter writes the named form everywhere else, the readers accept both. The converter recipe bumps (specdoc/1specdoc/2), so every transformed slot re-materialises by the derived-manifest law rather than lingering in the old shape. The owner's next call arrived the same day — facts follow, see ##NAMED-FACT-ELEMENTS`. Landed: the emitter writes named sections everywhere the predicate allows (the live redbook README golden carries 7 named / 0 generic), both readers accept both forms, and the engine mirrors the predicate verbatim across the separability seam.
17 Decision (ADR-part; owner ruling 2026-08-22, near-verbatim): «Не правильней ли не включать внутри list элементы item, а сразу ставить в тело list элементы типа <THE-LAW fact="true"...>? И вместо <list> использовать тэг <facts> — это новое слово для зарезервированного словарика… Если же список состоит из обычного текста (там могут даже иногда встречаться факты), то все элементы — это item и группировка — list, а факты в нём рендерятся как сейчас». The law: a list whose every item is exactly one meaningful fact materialises as the vocabulary element <facts ordered="…"> with the fact elements (named or generic) directly in its body — no <item> wrappers; any other list (plain text, or mixed with occasional facts) keeps today's <list>/<item> shape with facts rendered inside items. facts joins the reserved vocabulary (an anchor named facts falls back to the generic form); ordered carries over exactly as on <list>; the model is unchanged — both shapes parse to the same Block::List, so the reader accepts BOTH forms (old materialisations in the wild stay readable) and a rewrite normalises the all-fact shape to <facts>. Loud errors guard the grouping: a non-fact child inside <facts>, bare text inside <facts>, an empty <facts>. Both readers — the pivot and the specmap engine's native one — learn the form mirror-wise; the converter recipe bumps specdoc/3specdoc/4 and the host re-materialises once. Landed: the writer branch, the facts_block parser (split into the pivot's own xml_facts.rs along the engine's seam) and the vocabulary word sit in both readers with the four loud errors pinned; the engine proves model-identity by content hash between the two shapes; the redbook README golden re-pins with two <facts> groups and the same 45 named facts; the host's 37 slots re-materialised at specdoc/4; and the §5a stand re-ran as the regression tool it was left as — polygons rebuilt on the new shape (121 files carry groups), the sensitive tier (gpt-5.5@low) swept 9/9 with the negative control clean.

18The status on each unit says where it stands: spec/done for a settled rule, impl/done once code implements it, spec/work for a draft. Two id registers carry the signal at no cost: an upper-case id marks a rule with binding weight, a lower-case one a lead-in or a note.

19 Decision — two anchor-id registers (owner ruling, 2026-07-24). @fact:UPPER-SLUG names a normative fact (a law, rule, carrier, changelog entry — content with binding weight); @fact:kebab-case names a service unit (status lines, lead-ins, connective prose).
20
vibe refactor convert-source --from md --to xml --dry-run vibevm/vibespecs
dry-run ir-stable-loss vibevm/vibespecs/NOTES-FLOW.md
--- source
+++ reverse-projection
@@ -3,2 +3,3 @@
 @fact:ONE-NOTE One note per review. @status:spec/done

+

dry-run ir-stable-loss vibevm/vibespecs/boot/10-tool-review-notes.md
--- source
+++ reverse-projection
@@ -1,6 +1,5 @@
-<!-- vibe:static org.org.acme/review-notes — boot snippet -->
-
 # review-notes

 A `tool` package.

+

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

21vibe refactor convert-source --to xml or --to markdown converts files or whole folders, skipping the dependency tree and generated files. It writes the sibling form and deletes the original in one act, so the tree never holds both forms of one document. Before writing, it converts the result back and compares: a byte-identical round trip converts silently, and a loss of comments or layout is refused with a per-file description unless you confirm, or pass --force. A change of meaning is always refused, because that is a defect of the converter, not of your file. --dry-run reports every file and writes nothing.

22 vibe refactor convert-source --to xml|markdown [PATH…] converts spec sources between the two PROP-045 serialisations (md accepted as an alias of markdown). Each PATH is a file or a directory; a directory walks recursively and selects spec sources (is_spec_source) currently in the OPPOSITE form; a file in the target form already is reported already and skipped. The walk always skips: vibedeps/ (##VIBEDEPS-NEVER-CONVERTED), dot-directories, target/, and any file whose leading lines carry the generated by vibe marker (generated artifacts regenerate; they are not authored sources) — an EXPLICIT file argument overrides only the marker skip, never the vibedeps/ skip.
23 A conversion writes the sibling serialisation and deletes the original in the same act, so the one-document-one-form law (PROP-045 ##TARGET-MIXED, the pair-collision check) holds at every instant — the tree never holds X.md and X.xml together, not even transiently between files. The verb assumes version control underneath it and keeps no backups of its own.
24 The destructiveness check is the owner's law, implemented by reverse reconversion through the pivot: for a source S, parse to IR, emit the target form, read the target form back, and project it into the SOURCE form again; compare. Three classes: (1) byte-stable — the back-projection equals S byte-for-byte: converts silently. (2) IR-stable loss — the back-projection re-parses to the SAME IR but differs in bytes (dropped MD/XML comments, normalised layout): the verb REFUSES with a per-file description of what is lost, and proceeds only on interactive confirmation or --force. (3) IR-divergent — the back-projection re-parses to a DIFFERENT IR: always refused, --force does not apply; that class is a vibe-specdoc defect to file (the pivot broke its own round-trip law), never a corpus to damage.
25 On a TTY, class-2 files prompt per file (yes / no / all); off a TTY, class-2 without --force is an error listing every lossy file and what each loses. --force waives class 2 only. --dry-run classifies and reports every file, writes nothing, and always exits 0 (an inventory, not an attempt); a real run exits 0 iff every requested conversion landed, non-zero when any file was refused or declined.

Directives: use, embed and read

26A spec may pull another by address. #use spec://… pulls the whole top-level section that contains the addressed node, and not its siblings; #embed splices exactly the addressed node, no more. The default merge is :add, so interface text need not be repeated to appear in the result. Every file a directive names must be declared in the package's manifest. Cycles are legal in the contract layer and forbidden among source bodies, which is what guarantees that a build always ends.

27 #use pulls the whole top-level anchored ancestor of the addressed node — reference a subsection, load its enclosing top-level section as one connected block. It does not pull that ancestor's siblings; siblings are read only when themselves needed.
28 #embed has arbitrary granularity — it splices exactly the addressed node, no more.
29 Default is :add (absent a :-suffix) — so the interface text need not be duplicated to appear in the result.
30 Every file referenced by any directive MUST be declared in the package's vibe.toml (the directive graph and the manifest cannot disagree).
31 Invariant. The contract layer is where cycles are legal; the source layer is where topological order is mandatory. This is the theoretical no-deadlock guarantee: as long as the contract hierarchy is acyclic-under-#embed and no source body participates in a #use cycle, the build always terminates.

32In prose an agent reads, @spec://… with the at-sign is mandatory: the agent reads it in place, once, on first encounter, and the compiler never splices it. A bare spec://… is at the agent's discretion.

33 @spec://… (an @ before spec) is an in-place use: the agent MUST read it (mandatory), exactly as if a #use had been declared at the top of the file. Amendment (2026-08-24, the normal-flip ruling): the obligation is the AGENT's, not the AOT compiler's — a @spec target is never spliced into a compiled lane; the address itself is the compiled artefact, and the §2 equivalence invariant is carried by the agent's read obligation. (Splicing every @spec realised ##OPEN-CLOSURE-EXPLOSION on the first normal-format host build: the lane grew 250 KB → 2.5 MB off prose pointers.) Only an explicit #use is a compiler splice edge.
34 Read once. An @spec target is read only on first encounter, never re-read blindly — see the read-set (§below).
35 Bare spec://… (no @) is at the agent's discretion — read it if useful, skip it otherwise. (A future algorithmic agent narrows this further.)

An address never moves

36Once published, an anchor is immutable. Renaming a section or a rule leaves a tombstone at the old anchor pointing at the new one, so a citation written last year still lands. Moving files does not change addresses either: the address is logical, the path is physics.

37 L1 — physics moves, addresses do not. spec:// addresses, anchors, fact ids, specmap edges, scope! citations and recorded verdicts are LOGICAL and do not change: only the physical prefix maps differently (vibevm/vibespecs/common/PROP-000.xml still canonicalises to the same common/PROP-000 document path). Any relayout step that would rename an address is a defect of the step.
38 Anchors are immutable; a rename is a tombstone — for published documentation pages as for specs, and for translations, which must match their source.

Edge cases and rules

39A generated file is never a citation target: cite the source document, not the compiled boot lane.

40 A generated STATIC.md is not a citation target — authored text never cites spec://…/boot/STATIC#…; the lane is compiler output, and source-of-truth is the package source under vibedeps/ (PROP-035 §11's lint, B-011 §6.1).

41vibe facts check validates the markup of a package's specifications; vibe check validates the package as a whole; both run before publishing.

42Code may cite specifications too, with an attribute on the item that implements a rule, and a map then answers which code implements which rule in both directions; the architecture pages of this manual explain that map.

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-docs@1.0.0/authoring/specs-agents-can-cite

.md.xmlllms.txt