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

PROP-045: XML spec sources and materialisation targets

1. Mandate

01The owner's mandate, verbatim (2026-08-21): «XML как источник спецификаций (вместо Markdown). Везде где пользователь использует Markdown можно использовать XML. … Можно смешивать XML и Markdown в одном проекте. При материализации, XML превращается либо в Markdown (с минимальной деградацией качества — использовать вложенные секции/заголовки, все что невыразимо в такой форме — не поддерживается), либо в XML рядом с Markdown, либо в XML. … Формат материализации — настройка пользователя. … Лоадеры должны корректно обрабатывать все три вида материализации: XML, Markdown, Mixed (XML + Markdown). … XML как целевой формат материализации должен быть целевым для разных видов исходников. Всё, даже Markdown, при материализации превращается в XML. … Markdown материализация все ещё должна поддерживаться, это важно. … мы целимся в то, что Mixed Input (XML + Markdown) должен нормально транслироваться в XML (не в mixed!), и это станет в будущем основным форматом материализации». Acceptance named in the same mandate: a small test project importing org.vibevm.world/redbook, exercised in all three materialisation modes (XML+MD→XML, XML+MD→Markdown, XML+MD→Mixed), all well-tested — «это большое изменение».

02The direction was pre-recorded before the mandate: PROP-043 §8 `##PARSE-XML-GRAMMAR» holds that the markup element grammar is XML and that «a future XML storage frontend consumes the same attribute schema natively; the markup language does not change». This PROP builds that frontend and widens it from elements to whole documents.

2. The shape — one pivot, two frontends, two backends

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

04Decision (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.

05The dialect, first cut (построй и уточни: точные имена элементов/атрибутов финализируются с первым золотым корпусом):

06<spec xmlns="https://vibevm.org/spec/1">
  <title>PROP-NNN: …</title>                      <!-- the H1 -->
  <status stage="spec" state="work" comment="…"/> <!-- the existing element, verbatim -->
  <section id="anchor" title="2. The laws">       <!-- Hn nesting = section nesting; {#x} = id -->
    <p>plain prose; inline Markdown conventions ride as literal text</p>
    <p><fact id="NAME" status="impl/done">the fact body, one unit</fact></p>
    <list ordered="false"><item><fact id="N2" status="spec/done">…</fact></item></list>
    <table><tr><td>cells are countable units, as in MD</td></tr></table>
    <fence lang="rust" fact="N3">code; fact= is the @fact/code binding</fence>
    <quote>blockquote unit</quote>
  </section>
</spec>

07Decision (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.

08Decision (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.

09Terminal requirements cross the pivot as fact metadata, not status metadata. A Markdown fact's trailing @requires: set and an XML named/generic fact's requires="…" attribute lower to the same optional canonical artifact-kind set from PROP-043 ##REQUIRED-ARTIFACT-KINDS. The set survives every Markdown→pivot→XML→pivot→Markdown polygon exactly; absence survives as absence. The existing semantic fact ref follows the same polygon through Fact.status.ref: Markdown spells it on the final status point marker and XML spells it on the named/generic fact element; external requirements admit exactly that one non-empty locator. A standalone document/section <status> cannot carry requirements, and the closed XML dialect rejects requires on any non-fact element. This is the format-neutral carrier for PROP-043 ##REQUIRES-GRAMMAR, not a second terminality implementation.

10Decision (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.

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

12Decision (ADR-part): the XML machinery rides quick-xml, one new workspace dependency, pinned. Measured 2026-08-21: the tree carries NO xml crate anywhere (Cargo.lock grep across quick-xml / roxmltree / xml-rs / xmlparser — zero hits), so the frontend/backend need one. Alternatives weighed: roxmltree (a clean read-only DOM — but this PROP needs an EMITTER as much as a parser, and roxmltree has no writer), xml-rs (both directions but dated and slow), hand-rolling (a parser for a security-adjacent input format is exactly what one does not hand-roll). quick-xml carries both an event reader and a Writer with correct escaping, is maintained and widely fuzzed, and its event model fits the pivot walk. The version is pinned at the S1 landing with the workspace's usual workspace = true discipline.

13The heavyweight question, answered on the record (owner's challenge, 2026-08-21 — the Java instinct «сразу брать мощный навороченный парсер»). The Java-world case for a Xerces-class engine is the SOAP-era grammar surface: DTD, XSD, XInclude, catalogs. This dialect OUTLAWS that surface by construction — a closed ~13-element vocabulary where a foreign construct is a loud error, no DTD, no external entities — so a heavyweight would buy capability this contract forbids, while its Rust incarnation (libxml2 FFI bindings) would pay a C build dependency and libxml2's CVE record inside a security-adjacent input path. Three recorded consequences: (a) the XXE attack class dies by construction (quick-xml does not process DTD — here a feature); (b) validation is OUR closed-vocabulary walk with contract-citing errors, not a schema engine's; (c) conformance is proven on OUR documents — the golden corpus and round-trip property tests over redbook — not assumed from the parser's reputation. Escape hatch, explicit: the frontend is one module behind the pivot seam; if the reader shows conformance holes, it swaps to roxmltree (the ecosystem's conformance-strongest read-only DOM) while the quick-xml Writer stays — a bounded change that alters no consumer. quick-xml itself is the ecosystem's de-facto standard (the most-used XML crate on crates.io by a wide margin; calamine, the RSS stack and docx readers ride it), not a first-hit pick.

3. The materialisation setting and the three targets

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

15markdown target: XML sources emit as Markdown through the pivot (nested sections → heading levels, facts → anchored units, fences/tables/quotes → their MD forms); MD sources copy verbatim. This target exists for tooling that cannot read XML or mixed trees — named in the mandate as load-bearing, and it stays supported for as long as this PROP stands.

16xml target: every source — including Markdown — emits as dialect XML through the pivot. Mixed input translating into CLEAN XML (never mixed output) is this target's acceptance bar, because it is the future primary.

17mixed target: copy-through; each file keeps its authored format.

18STATIC obeys the same materialisation rules as everything else (owner ruling 2026-08-22, chat, near-verbatim: «при рематериализации у нас STATIC.md в формате Markdown вне зависимости от того, какой формат материализации выбран, это плохо. На STATIC должны действовать те же правила, что и на всю остальную рематериализацию — должен появиться STATIC.xml»). The generated static lane emits in the project's materialisation format: STATIC.xml under the xml target, STATIC.md under markdown (the mixed extension is the STATIC-format wave's open call — PROP-046 ##OPEN-MIXED-STATIC). This owner-revises the extension-stable-STATIC.md residue named at the inheritance-parity landing (##INHERITANCE-PARITY): what was lawful residue is now a defect to fix. The wave's perimeter, known in advance: the dynamic-STATIC target path in hybrid emit, the bootgen emitter, the generated CLAUDE.md boot block, INDEX raw-snippet targets, the parity twins that pin the .md name, and every by-name consumer of vibevm/vibespecs/boot/STATIC.xml (hooks, docs). Ordered by the owner as the next act after PROP-046's status waves. The compilate-form decision (recorded before the build): STATIC is and stays an AGENT-facing tape of contributions — under the xml target each contribution emits through the pivot's to_xml instead of the markdown projection, the <!-- vibe:static … --> provenance separators stay comments, and a single well-formed root is deliberately NOT promised (the compiled lane is not an input to the machine readers today, and wrapping thirty documents under one root would change anchor semantics for no consumer); the extension law is xml → STATIC.xml, markdown → STATIC.md, mixed → STATIC.md (the single canonical compilate for a per-file target, resolving PROP-046 ##OPEN-MIXED-STATIC); exactly one STATIC file may exist — both present is a loud generator error; consumers learn the name from the generated sources (the CLAUDE.md boot block, INDEX, the redirect) or by exactly-one existence, never by guessing. Landed (7d494f53 + 40e67d56): the format module owns both names with the stray deleted on a switch (polygon-pinned across all three targets), contributions cross to_xml once post-qualification with the markdown path byte-identical, every chooser and generated-name exclusion selects through the module, exactly-one resolution replaced guessing in every machine reader (a new one — show effective — was found by the build itself and reads the lane as the tape it is), boot_directory fails loud on both names, and the host lane re-landed as STATIC.xml with 26 contributions.

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

20Boot-lane law under the setting (revised by the owner's scenario ruling, 2026-08-21, and completed by ##STATIC-FOLLOWS-THE-TARGET). Boot artifacts are generated projections. The static compiler consumes snippets in WHATEVER format the slots materialised (an XML-materialised snippet pivots through the shared IR) and emits the selected target format: STATIC.xml for xml, STATIC.md for markdown|mixed. The dynamic lane's entries point at the files as materialised — under spec_format = "xml" a dynamic INCLUDE target IS an .xml file, and INDEX.md carries that path honestly. The dynamic ROUTER is the reading agent itself (PROP-009: boot is pure file-reading; a dynamic entry is an INCLUDE resolved by the reader), which is why §5a measures agents, not code. The former “compiled static stays Markdown” / “fully-XML is follow-up” wording is superseded; the XML polygon and current host STATIC.xml are its landed proof.

21The owner's named scenario — acceptance case №0 (ruling 2026-08-21, verbatim: «все спеки в spec написаны в Markdown, внутренние пакеты в packages — в Markdown, а формат материализации — XML. Лоадеры и все остальное должны быть к этому готовы. и статические, и динамические, все»). Sources are 100 % Markdown (the host spec/ tree and every packages/ member); spec_format = "xml"; after install/materialisation: every vibedeps spec file is dialect XML, the static lane compiles clean from those XML snippets, the dynamic lane's INCLUDE targets are .xml and resolve, and every reader — vibe progress check, specmap, vibe check, `vibe tree», the boot readers — is green over the result. This is the polygon's primary run; the three mixed-input runs of §5 ride beside it.

4. Loaders and scanners read all three

22Every consumer of spec sources — the progress scanner and vibe progress check», the specmap unit parser, vibe check, the tree/ boot readers, mirror views — accepts .md and .xml spec files and a tree mixing both. XML goes through the XML frontend into the same pivot the MD frontend feeds, so every downstream mechanism (facts, verdicts, staleness hashes, unit counts, anchors, specmap units) works unchanged on either source. *Построй по замеру: точный список интеграционных точек (.md-фильтры) даёт XML-MEASURE; каждая точка получает парную .xml» ветку, ни одна — молча.

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

24spec:// addressing is format-blind: anchors are id attributes in XML and {#…}/first-token anchors in MD, minted into the same address space; a document's address does not change when its serialisation does.

5. The redbook polygon — acceptance

25A dedicated test project imports org.vibevm.world/redbook (the largest real corpus of house-style markup) plus locally-authored XML and MD specs, and the suite drives all three targets end-to-end: (a) XML+MD → xml: every materialised spec file is dialect XML, goldens pinned, and the round-trip MD→XML→MD over redbook files is semantically stable (units, facts, anchors, statuses, tables, fences — counted equal; the degradation measure); (b) XML+MD → markdown: every file is MD, XML-authored sources render with nested headings per the degradation law; (c) XML+MD → mixed: byte-identical copy-through. In all three, the loaders prove themselves: vibe progress check clean, specmap builds, boot regenerates.

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

5a. The dynamic-router measurement — external agents

27The hardest part, named by the owner (2026-08-21): measuring the dynamic routers in EXTERNAL agents. The dynamic lane has no code router — the reading agent resolves dynamic INCLUDEs itself — so readiness for XML targets is an empirical property of live agent harnesses, not of this repository's code, and it is MEASURED, not assumed. At measurement time the instrument used one Claude-family external lane and codexrunner as the GPT-family lane. The former subscription-backed lane was retired by owner ruling 2026-09-08 and is not a current or repeatable runner; the reusable stand now exposes only its Codex path. Protocol: the polygon project (scenario №0 state — XML-materialised tree, honest INDEX.md with static and dynamic entries, at least one when-guarded conditional entry) plus a probe packet that orders a cold worker to perform the standard boot (read STATIC in full, then every INDEX entry, resolving dynamic INCLUDEs) and then answer control questions whose answers exist ONLY inside dynamically-included XML files (one per dynamic entry, plus one inside a when-inactive entry that must NOT be answered — the negative control). Scoring is by artifact: each answer cites the file it came from; the measure is answered/missed per lane, per agent family. The run is repeated over the markdown» and mixed» materialisations of the same tree as the baseline — the DELTA between XML and MD scores is the finding, not the absolute number. Results are recorded in the polygon's report and this section's facts flip to impl/… with the measured numbers cited. Проверь при постройке: probe не должен подсказывать формат — пакет говорит «выполни бут по CLAUDE.md/INDEX», не «прочитай XML». MEASURED (2026-08-22, on the final dialect — named sections and facts, specdoc/3): 36/36 PASS, XML−MD delta = 0 everywhere. The polygon (redbook closure + three org.vibevm.probe flows: two os:windows beacons, one os:linux negative control) ran three materialisations × three lanes — codex gpt-5.5@low ×2 sweeps, codex gpt-5.6-sol@xhigh, claudez GLM big — with a format-blind probe; every beacon answered with its exact materialised source path, and the inactive entry was correctly reported unreadable in all nine cells with zero beacon leakage. Report: campaigns/packages-2026-09/xml-measure/RESULTS-2026-08-22.md; the harness is a reusable regression stand (setup.sh + the weak-tier sweep as the sensitive detector).

28Model tiers are a measurement dimension — the simpler models go first (owner's advice, verbatim, 2026-08-21: «при измерении воркеров через Codex я бы советовал попользоваться не моделью gpt-5.6-sol, а в первую очередь более простыми моделями, чтобы проверить насколько они вообще справляются с новыми режимами»). A strong model masks format friction; the weak model is the sensitive instrument. The Codex-lane probes therefore run the SIMPLER available tiers first (CODEXRUNNER_MODEL/CODEXRUNNER_EFFORT are the launcher's overrides; the measurement slice enumerates which tiers the installed codex actually serves and records the list), with the pinned strong tier (gpt-5.6-sol) run last as the ceiling reference — the per-tier score table, not one number, is the deliverable. The symmetric extension on the Claude-family lane (a small slot exists there too) is the builder's own addition, applied with the same first-simple ordering unless the owner says otherwise. The lane DEFAULT for work tasks is untouched: this tiering is the measurement protocol's, not the launcher's. MEASURED (2026-08-22): the installed codex serves exactly two tiers — gpt-5.5 and gpt-5.6-sol (gpt-5.5-codex, gpt-5.1-codex-mini, codex-mini-latest error on the ping; list recorded in the stand's tiers.txt); effort served as the second simplicity axis. The sensitive instrument went first per the owner's ordering — gpt-5.5@low, two full sweeps, 18/18 — and the ceiling gpt-5.6-sol@xhigh matched it 9/9: the per-tier table shows NO tier gradient, i.e. the final dialect's format friction sits below even the cheap tier's noise floor. Claude-family symmetric run: claudez big 9/9.

5b. Questions the build surfaced — dispositions

29REVIEW markers and comments (S4 finding, disposed). The dialect legally SKIPS XML comments as layout and the pivot deliberately drops them, so a projection loses <!-- REVIEW: --> markers. The law therefore is: comment-consuming readers (review aging, managed-block scanners) read RAW SOURCE TEXT of both forms — the comment syntax is shared by MD and XML — and never the projection; S4 built review aging exactly so, with source-relative lines. A comment-carrying pivot was weighed and refused: comments are the one construct whose whole point is to be invisible to the document model.

30The normal boot format over XML slots is named residue (S4 finding, open). compile_normal_entry reads its closure through vibe-spec's own MD section source, which the projection cannot feed without touching that crate; simple snippets and authored boot files carry XML materialisation fully today. The residue rides the S4b family (the engine/vibe-spec lane), recorded here so scenario №0's polygon states honestly which snippet formats its packages use.

31show effective's boot-origin match is logical-document keyed (S4 finding, closed by S5). The literal-name match silently degraded a transformed snippet's origin to user»; the S5 landing keys the match on the logical stem (10-flow-wal.xml and 10-flow-wal.xml are one contribution), pinned by the polygon's origin test — a snippet materialised into .xml` reports its package.

32Generated boot artifacts are outside the derived identity (S5 polygon finding, law). Boot regeneration writes a child target-selected vibevm/vibespecs/boot/STATIC.xml|STATIC.md plus INDEX.md INTO a dependency slot after materialisation — so the derived hash excludes them: they are outside the slot record's owned payload, just as the record file itself is outside content identity. The format-purity claim never counts them: a transformed slot's «no foreign-form spec files» is asserted over SOURCES, not over projections the machine regenerates at will. The polygon caught both failure shapes live before this law existed: a stale derived hash the moment boot regenerated, and a fake purity violation on the one slot whose snippet compiles to a child STATIC.

6. Build order

33S1 pivot + XML frontend/backend + golden round-trips over the redbook corpus → S2 MD backend (XML→MD) + degradation tests → S3 the setting + transforming materialisation + the hash law → S4 the scanner/ checker/specmap/boot-compiler integration points (the measured *.md list; the static splice learns XML input, INDEX carries materialised paths) → S5 the redbook polygon E2E: scenario №0 first, then the three mixed-input targets → S5a the historical external-agent router measurement (§5a, two agent families over the polygon; only the Codex path remains runnable) → S6 docs, ALPHA-NOTES, judging. Each slice lands with its own gates; the polygon plus the agent measurement are the wave's exit.

7. The documentation vocabulary — the dialect reopened for one genre

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

  • 35The vocabulary is a parameter of the reader, not of the document. Vocabulary::{Spec, Doc} (default Spec) selects the accepted element set through additive entry points (from_xml_with, load_spec_text_with, project_spec_text_with); the mapping «package kind → vocabulary» is the caller's, because the pivot knows no PackageKind by the separability law. A document declares nothing about its vocabulary.
  • A documentation element met under the Spec vocabulary is a loud error with a genre-naming message («open only in packages of kind doc»), never a silent skip — the same closed-vocabulary law as everywhere in the dialect.
  • The discriminator against name collisions. example, rule, derived and run already live in the corpus as named sections (<example title="…">). The rule: a documentation block never carries title=; a named section always does. <example title="…"> is a section under any vocabulary; <example> without title is a block under Doc. The writer does not depend on the vocabulary: the elementability blacklist does not grow, and one IR serialises to the same bytes under both vocabularies.
  • when is a property of the slot, not of a block kind: Section.blocks and SpecDoc.preamble hold BlockNode { when: Option<Cond>, block }, so a condition applies to any block and to sections. Cond is a closed list — the boot lane's condition vocabulary (os:…, agent) — with a nearest-legal hint on error.
  • The Markdown projection of the documentation genre is one-way, and this is pinned. md_out emits the best projection of every element (the table below); parsing that projection back yields a different IR (Conversion::IrDivergent), and the expectation test doc_genre_is_not_round_trippable_through_markdown pins it. The projection exists because the host scanners (facts check, progress) read XML only through it (##PROJECTION-READ) — the documentation stays observed. Authoring documentation is XML only; md_in is not widened, and cannot be: the Markdown block kinds are closed in progress-core.
  • The texts of run, expect, stderr, the body of prompt and each assert are verbatim like Fence::text; CDATA is admitted in exactly these elements by an explicit list.
  • The address of a rule drops any ~rN revision suffix: a pin that strays into the attribute never becomes a pin on the documents edge (PROP-057 §14 — citations are live and unpinned).
  • There is no tabs element on purpose; when replaces it. Step-by-step procedures are an ordered list with example blocks inside. Inline content stays Markdown (##INLINE-STAYS-MARKDOWN).
  • Known limit: the README and specs of packages of other kinds cannot carry verifiable examples — the vocabulary opens by package kind. Their fences render at level 0 as they are, unverified.

36The elements, their Markdown projection and their checker (the semantics of each checker are PROP-057 §10 and §14):

37
Element Purpose Markdown projection Checker
example with children run, expect (stdout) and optional stderr; attributes id, fixture, lang, exit (default 0), when a command and its expected output; fixture names a hermetic fixture project that declares the normalisation rules and the map «--json document → JTD schema»; an absent stderr asserts «stderr is empty» two adjacent fences, sh and output (a third, stderr, when present) the runner vibe doc check --examples against the built binary: exact match after the declared normalisation, no match templates; a divergence is red
example ref="<id>" in a translation: a reference to the source's example instead of an example of its own the same fences, copied from the source at projection time the id exists in the source
rule ref="spec://…#ANCHOR" the insertion point of a rule from a specification; the site shows the fact's current text in place, in the specification's language; the citation text is not pivot state — it is substituted at build a link to the anchor, as a quote with the address the anchor resolver; a documents edge without a pin; a vanished anchor is a build error
derived kind="cli-help | jtd-schema | manifest-field" ref="…" the insertion of a machine-derived reference or a manifest field (for example abstract); the text is not stored a fence with the text, marked «generated from …», or a table the generator at build; a divergence is red except under an explicit --accept
note kind="note | tip | warning" a call-out; its body is a unit and therefore addressable by a fact anchor a quote with the kind label in its first line the schema
figure src alt with a child caption an image with a caption; the file is a source file in the package tree; the caption is a unit an inline image plus a caption paragraph the file exists and passes the media rules of PROP-057 §7
prompt id with a body and children needs, outcome, assert (one or more) a task for an agent in the user's voice; what the agent needs; what the person sees when it worked; shell commands that must exit zero after the agent's work a prompt fence, a «needs» list, an «outcome» paragraph, an assert list; the same fence in llms*.txt vibe doc check --prompts: a run by the configured agent in a clean temporary directory, then the asserts; outside the panel; on a scenario page an assert is mandatory (the style linter)
the attribute when="os:…" on any block and on sections platform and agent variants a sub-heading named after the platform the existing condition vocabulary of the boot lane
  • 38Why: the owner chose the named-element form — a named tag is self-describing for an agent, and an example is verifiable by construction. A second vocabulary rather than a widened one keeps the spec dialect exactly as measured and pinned; gating by kind keeps a flow README from silently acquiring unverified «examples».
  • Considered and rejected: Markdown with directives; conventions inside the present dialect (a fence pair «by agreement» — nothing can check it); a tabs element; own examples in translations; widening md_in (the Markdown block kinds are closed in progress-core).
  • Revisit when: two authors' requests for a construct outside this set, or for verifiable examples in the README of an ordinary package, are recorded in BACKLOG.

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-045-xml-spec-sources

.md.xmlllms.txt