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

G2-MECHANICS — механика эволюции формата: второй взгляд

01Это второй независимый разбор свода FINDINGS-DIGEST.md. Предмет — как наш формат меняется во времени, не ломая читателя. Самый ценный результат здесь — обоснованное несогласие; где я согласен, я добавляю того, чего в своде нет.

02Правило разметки источников. У меня нет интернета, поэтому: [ИЗ СВОДА] — из FINDINGS-DIGEST.md; [ИЗ ДЕРЕВА] — проверено мной чтением кода, с файл:строка; [ПО ПАМЯТИ, НЕ ПРОВЕРЕНО] — мои знания о чужих системах, без выдуманных версий/дат/номеров.

TL;DR — где я не согласен

  1. 03Avro для нас — дороже, а не «почти бесплатно». Предпосылка свода «схема и данные в одной истории git» неверна: каталог лежит в реестре, схема (Rust-типы) — в исходном репозитории vibevm. Это две разные истории. Внешний читатель без репозитория и скопированная наружу запись ломают модель Avro целиком — остаётся терпимый читатель, который у нас уже есть.
  2. Объединение НЕ «угадывает по набору ключей». В текущем дереве вариант Directory уже несёт позитивный дискриминант kind:"directory" (crates/vibe-index/src/types/repomd.rs:44-50). Настоящий дефект — #[serde(untagged)], и он устраняется сменой одного атрибута.
  3. «14 полей-коллекций» — заниженный периметр. С skip_serializing_if = "...is_empty" в дереве 21 поле, не 14. Решение А6 принималось по числу 14; для языка схемы важны все 21.
  4. Невосстановление присутствия у списков в protobuf — осознанное, а не «незавершённость». И нам для каталога оно не нужно: машинные поля не бывают legitimately «неизвестными».
  5. «Мы наследуем проблемы JSON-кодировки protobuf без их выхода» — неверно по рамке. Наш выход — убрать deny_unknown_fields (один атрибут), а не двоичный формат. Скалярного коллапса proto3 у нас в JSON нет вовсе.
  6. Идентичность поля именем — компенсируется. Свод предложил список отставленных имён; этого мало для rename-безопасности. Настоящий механизм — #[serde(alias = …)], и он бесплатен в JSON и TOML.

0. Что я перепроверил по дереву (доверие своду — с поправками)

04Свод верен в конструкции, проверенной мной [ИЗ ДЕРЕВА]:

  • 05deny_unknown_fields в каталоге — ровно 15 в crates/vibe-index/src (repomd.rs:1, entry/relations.rs:6, entry/mod.rs:1, entry/content.rs:5, entry/aggregate.rs:2). Совпало со сводом.
  • #[serde(other)]нигде в дереве (подтверждает и campaigns/packages-2026-09/harvest/a6-wire-format-census.md:359-360). Совпало со сводом.
  • Версия каталога — ярлык, а не переключатель. Единственное сравнение версии во всех .rs: crates/vibe-core/src/manifest/lockfile.rs:430 if lockfile.meta.schema_version != CURRENT_SCHEMA_VERSION — это lockfile. Каталог читается через голый serde_json::from_slice (crates/vibe-index/src/index/repomd.rs:32-39), версия не сравнивается ни разу. Совпало со сводом.
  • Клиент терпим, себе строги. crates/vibe-registry/src/index_client/wire.rs — узкие view-структуры (VersionEntryView читает только version, wire.rs:30-33), #[serde(default)], без deny_unknown_fields; комментарий wire.rs:37-39 прямо говорит: «Extra fields on the wire … are tolerated silently — kept simple so a server-side envelope addition does not force a client bump». Совпало со сводом.

06Поправки к своду [ИЗ ДЕРЕВА]:

  • 07Объединение описано неточно. Свод: «Общего поля-признака нет; читатель угадывает по набору ключей». В дереве:
08  // crates/vibe-index/src/types/repomd.rs:41-55
  #[serde(untagged)]
  pub enum RepomdFileEntry {
      Directory {
          /// Always the literal string `"directory"`. Carrying this as
          /// a tag inside the directory variant lets serde's `untagged`
          /// matcher distinguish unambiguously.
          kind: DirectoryTag,
          entries: u32,
      },
      File { size: u64, sha256: String },
  }

09У Directory есть позитивный тег; «по набору ключей» угадывается только File. Буква «общего поля нет» верна, но вывод «угадывание» — преуменьшение.

  • 10«14» против «21». Полей с skip_serializing_if = "...is_empty" в crates/vibe-index/src21:
  • Vec::is_empty — 12 (relations.rs:18,31,44,46,65,78; entry/mod.rs:72,78,96,108; content.rs:68,75);
  • BTreeMap::is_empty — 2 (content.rs:39,41);
  • *Entry::is_empty (структурные) — 7 (entry/mod.rs:87,90,93,99,102,105,111). Свод считает только сырые коллекции (12+2 = 14); но структурные поля вроде provides: ProvidesEntry коллапсируют «пусто≡отсутствует» точно так же: когда структура пуста, поле пропускается (entry/mod.rs:90, ProvidesEntry::is_empty в relations.rs:35-39). Для языка схемы релевантны все 21. Периметр «сырые коллекции» оправдан, но число, на котором решали А6 (14), занижено относительно того, что схеме предстоит выразить.

11Не перепроверял и не считаю нагрузко-несущим для своих выводов: «23 типа / 18 каталожных / 86 полей». Принял из свода как есть.

1. Avro — насколько хороша для нас на самом деле? (ДЕШЕВЛЕ ИЛИ ДОРОЖЕ ТЕРПИМОГО ЧИТАТЕЛЯ)

Что должен сделать читатель, чтобы модель Avro сработала

12Модель Avro [ПО ПАМЯТИ, НЕ ПРОВЕРЕНО]: каждая запись пишется против схемы писателя; читатель обязан добыть именно ту схему писателя, по которой записано, и согласовать её со своей схемой читателя. Обычно — по отпечатку (fingerprint), который лежит в данных, через реестр схем.

13Чтобы это работало у нас, читатель должен:

  1. 14Извлечь из данных идентификатор схемы писателя. У нас в repomd.jsonschema_version: u32 (repomd.rs:21), не отпечаток схемы. То есть читатель знает «версия 1», но не знает, какой именно текст схемы соответствует версии 1.
  2. Где-то этот текст найти. У нас его для каталога нет как артефакта[ИЗ ДЕРЕВА]: в crates/vibe-index/ нет .json-схемы; схема существует только как Rust-типы (types/) и спека-проза (PROP-005).
  3. Запустить согласование схем.

Предпосылка свода неверна: это не «одна история git»

15Свод [ИЗ СВОДА]: «в git-репозитории это стоит указателя, и история схемы лежит в той же истории, что и данные». Но:

  • 16Каталог (repomd.json, primary.jsonl, by-name/*.json) — это публикованный артефакт реестра; поле registry/registry_url (repomd.rs:22-23) указывает на репозиторий-источник. Схема (Rust-типы) живёт в исходном репозитории vibevm. Это два разных репозитория с двумя разными историями. История каталога не содержит текста схемы — значит, «одна история» ложно.
  • «Схема едет с данными» — тоже ложно сегодня: рядом с repomd.json нет файла схемы. (Контрпример из дерева: для vibe tree --json схема естьcrates/vibe-cli/resources/package-tree.schema.v1.json с $id и schema_version:{const:1}, плюс золотой тест crates/vibe-cli/tests/tree_json.rs. Способность публиковать артефакт схемы в доме есть — просто не применена к каталогу. Это и есть настоящий Avro-смежный пробел.)

Читатель без репозитория / запись, скопированная наружу

  • 17Внешний инструмент читает каталог из реестра; он не клонирует исходный репозиторий vibevm с Rust-типами. У него нет ни схемы писателя, ни пути к ней. Механика согласования Avro не запускается — он возвращается к «читать JSON как умею», то есть к терпимому читателю. Avro ему ничего не дал.
  • Запись, скопированная из репозитория наружу (строка primary.jsonl в баг-репорте, зеркало метаданных пакета): нет ни версии, ни контекста — чистый JSON. Avro невозможен; терпимое чтение — единственный вариант. А это самый реальный способ, которым нашими данными будут пользоваться.

Вердикт: ДОРОЖЕ

18Avro требует: публиковать машинно-читаемый артефакт схемы с данными (или реестр), учить читателей его доставать и согласовывать, версионировать этот артефакт, держать дисциплину отпечатков. Это большая машинерия — ради выгоды, которую у нас некому потреблять (внешних читателей ноль [ИЗ СВОДА], а своим согласование схем не нужно: писатель и читатель — один бинарник).

19Терпимый читатель требует: читать узкими view, игнорировать незнакомые поля, отсутствующее трактовать как default. Это уже написано (wire.rs). Маржинальная стоимость — около нуля.

20Ответ на вопрос: Avro для нас дороже терпимого читателя, заметно и по машинерии, и по предпосылкам. Настоящий (и полезный) урок Avro — не «возить схему с данными», а «сделать схему машинно-читаемым артефактом вообще». Этого артефакта у каталога сегодня нет; фикс — опубликовать JSON Schema / JTD (паттерн уже есть в vibe-cli/resources/), а не внедрять разрешение схем Avro.

2. «Пусто против отсутствует» — нужна ли нам различимость списков

Невосстановление у protobuf — осознанный отказ, а не недоделка

21Свод [ИЗ СВОДА]: для скаляров присутствие вернули через ~5 лет, для списков и словарей — «так и не вернули … слиты навсегда», и подаёт это как незавершённость.

22Я утверждаю, что это осознанный отказ, и вот цена различимости для списка [ПО ПАМЯТИ, НЕ ПРОВЕРЕНО]:

  • 23каждое поле-список получает третье состояние (нет / пусто / заполнено);
  • сгенерированный код несёт Option<Vec> или has-бит на каждое такое поле;
  • каждый читатель обязан решить, что семантически значит «нет» против «пусто» — а это решение не универсально;
  • любой инструмент, не понимающий присутствия, молча сколлапсирует их обратно (round-trip-несоответствие).

24Для proto3 цена особенно высока: их двоичный формат не выделяет присутствие для repeated-полей без явного бокового маркера, и реставрация ломала бы компактность и обратную совместимость с существующими данными. Отказ рационален.

Главный аргумент: для машинных данных «отсутствует» — не настоящее состояние

25Ключевое различие, которое свод не провёл: присутствие нужно человеко-написанным данным, где «я не заполнил» реально, и почти не нужно машинно-сгенерированным, где каждое поле детерминированно вычислено.

26Каталог пишет индексатор. Он всегда знает значение каждого поля — читает его из vibe.toml пакета. Поэтому «отсутствует» для каталожного поля — не легитимное «неизвестно», а всегда «вычислено как пустое». Различимость пусто/нет ничего не покупает, потому что состояние «нет» никогда не наступает законно.

27Конкретно по нашим полям [ИЗ ДЕРЕВА]: authors, keywords, requires.packages, provides.capabilities, features, i18n.available — все вычисляются индексатором из манифеста; «нет» у них не бывает. Даже requires_any (entry/mod.rs:96) — «зависимость any-of не задана» — это ограничение, а не присутствие.

Где различимость всё-таки нужна — и это не каталог

28У манифеста vibe.toml, который пишет человек [ИЗ СВОДА], «я не указал» — реальное состояние. Там authors = [] (явно: авторов нет) против отсутствующего authors (не записано) — осмысленное различие. И TOML здесь помогает, а не мешает: отсутствие null в TOML — это фича, она принуждает выражать трёхзначность через «нет ключа» против [], без null-сигнала, который можно перетолковать. JSON и TOML соглашаются в механизме.

Вердикт

29Различимость пусто/нет для списков нам для каталога НЕ нужна. 21 поле со skip_serializing_if = "...is_empty" безопасны для машинных данных. Присутствие стоит сохранять только там, где «неизвестно» легитимно — то есть в человеко-написанном манифесте. И свод сам к этому приходит в рекомендации №2 (null не нужен: в TOML его нет) — но он не развёл, что для каталога это безразлично, а для манифеста — обязательно, и фиксы там разные.

3. Тегированные объединения — что делать с RepomdFileEntry

30Текущая форма [ИЗ ДЕРЕВА] (repomd.rs:41-77): #[serde(untagged)], Directory { kind: DirectoryTag, entries }, File { size, sha256 }. На проводе {"kind":"directory","entries":N} против {"size":N,"sha256":"..."}. Дефект не в «отсутствии тега», а в untagged: плохие ошибки («did not match any variant»), асимметричная разметка (один вариант мечен, второй угадывается), и — главное для А6 — untagged трудно/невозможно выразить в обычном языке схемы.

31Ниже — пять конкретных форм с ценой. A, B, D — рабочие; C — ценовой baseline (текущее направление); E — отбраковывается.

Форма A — явное поле-дискриминатор (стандарт)

32#[serde(tag = "kind", rename_all = "lowercase")]
pub enum RepomdFileEntry {
    Directory { entries: u32 },
    File { size: u64, sha256: String },
}
33{ "kind": "directory", "entries": 42 }
{ "kind": "file", "size": 184522, "sha256": "..." }
  • 34Цена: каждое файловое действие дополнительно несёт "kind":"file" (байты); ломающее изменение файлового варианта (сегодня у файла нет kind) — но внешних потребителей ноль [ИЗ СВОДА], сейчас это даром.
  • Чем платим / получаем: чистые ошибки serde, оба варианта симметричны, форма выражается в JTD, JSON Schema (discriminator/oneOf), protobuf (oneof), Avro (union). Это ровно то, что разрешает блокер А6 — тип становится выразимым в языке схемы в тот момент, когда мы перестаём брать untagged. По сути — один атрибут от нынешнего состояния.

Форма B — разные ключи верхнего уровня (разделённые карты)

35"files":       { "primary.jsonl": { "size": 184522, "sha256": "..." } },
"directories": { "by-name": { "entries": 42 } }
  • 36Цена: одна логическая коллекция (files: BTreeMap<…>, repomd.rs:34) дробится на две; читатель обязан проверять обе; теряется единый детерминированный порядок обхода путей; путь не может мигрировать файл↔каталог без переноса ключа.
  • Чем платим: фрагментация API и двойная бухгалтерия. Зато каждый кусок однороден и строго типизирован. Урок PyPI [ИЗ СВОДА] («поле, которое бывает bool или dict → крах») аргументирует за явную структуру — разделение на две типизированные карты структурно явно, но ломает инвариант «один путь → одна запись».

Форма D — вложение в именованный объект (обёртка) — нет в своде

37Соседне-тегированная форма, родная для serde:

38#[serde(tag = "kind", content = "value", rename_all = "lowercase")]
pub enum RepomdFileEntry {
    Directory { entries: u32 },
    File { size: u64, sha256: String },
}
39{ "kind": "directory", "value": { "entries": 42 } }
{ "kind": "file",      "value": { "size": 184522, "sha256": "..." } }

40(Одно-ключевая форма {"directory":{…}} / {"file":{…}} тоже возможна, но требует ручной ser/deser — serde не даёт её из коробки; adjacent-тег — серде-нативный её аналог.)

  • 41Цена: лишний уровень вложенности и value-обёртка → больше байтов и глубины.
  • Чем платим / получаем: максимальная самодокументируемость (читатель видит единственный ключ и знает вариант, не сканируя поля) и чистая прямая-совместимость: третий вариант {"kind":"symlink","value":{…}} добавляется без двусмысленности. Самая «будущестойкая» форма; переплата байтами оправдана, если вариантов со временем будет больше двух.

Форма C — вывод вида по обязательному полю (baseline = текущее направление)

42{ "entries": 42 }
{ "size": 184522, "sha256": "..." }
  • 43Цена: это худшая форма — ровно то нетегированное угадывание, которому свод (справедливо) не доверяет, и ровно форма PyPI «bool или dict», которая роняла pip [ИЗ СВОДА]. Плохие ошибки, двусмысленность при совпадении имён полей в будущих вариантах.
  • Чем платим: включена сюда только как ценовой baseline, чтобы оценить остальные. По сути это нынешний дизайн минус тег у Directory.

Форма E — тег в ключе карты — отбраковывается

44"dir:by-name": { "entries": 42 }
"primary.jsonl": { "size": 184522, "sha256": "..." }
  • 45Цена: загрязняет пространство имён путей (путь — данные, не тег типа); путь, содержащий двоеточие, ломает схему; читатель парсит строки. Сильно не рекомендуется: путь — это идентичность и должен оставаться чистыми данными.

Рекомендация

46Форма A. Один атрибут от нынешнего состояния, стандарт во всех языках схем, дешёвая, и — критически — устраняет блокер А6 («тип невыразим в языке схемы»): он невыразим только пока мы держим untagged. Если ожидается рост числа вариантов — Форма D.

4. Асимметричная строгость — граница: терпимо к чему именно

47Свод (рекомендация №4) делит строгость по поверхности: строго на входной двери (vibe.toml, руками — ловим опечатку), терпимо дальше (каталог, машиной — переживаем будущее). Это верно, но НЕ проработано по видам «незнакомого». Разберу пять видов.

48Вид 1 — незнакомое ПОЛЕ (ключ). Терпимо = игнорировать. Это безопасный, стандартный, прямо-совместимый выбор [ПО ПАМЯТИ, НЕ ПРОВЕРЕНО]. Цена: опечатка в имени поля молча игнорируется (потеря данных этого поля). Парирование: строгость на двери авторинга (валидация vibe.toml), где опечатки и рождаются. → Терпим дальше.

49Вид 2 — незнакомое ЗНАЧЕНИЕ словаря (известное поле, новый член enum, kind:"plugin"). Спорное [ИЗ СВОДА]: Google — не ломающее, K8s/LinkedIn — ломающее. Механическая причина [ИЗ СВОДА]: неизвестное значение обязано лечь в типизированную ячейку, перечисляющую только известное; у нас без #[serde(other)] разборщик падает. Самая опасная терпимость — из аварии Cloudflare [ИЗ СВОДА]: старый читатель молча отображал неизвестное в default и уверенно считал неверно» («ботовый балл ноль»). Молчаливый default хуже отказа. → Граница: никогда не проглатывать неизвестное значение enum в default. Либо отказ (строго), либо явный вариант Unknown(String), который потребитель обязан обработать. Причём граница зависит от поля**: kind примыкает к идентичности пакета — неизвестный kind = читатель не может даже категоризировать, отказ правилен; delivery (content.rs:51-57, eager/lazy-push/lazy-pull) — это подсказка, не идентичность, тут Unknown + мягкая деградация оправданы. Рецепт LinkedIn [ИЗ СВОДА]: новое необязательное поле с новым enum, старые символы поддерживаются бессрочно.

50Вид 3 — незнакомый ТИП значения (строка там, где число, "size":"184522"). Это не эволюция, а порча. Несоответствие типа = фундаментальное расхождение схем; коэрцция («строка-как-число → число») — в точности патология «будь либерален» из RFC 9413 [ИЗ СВОДА]: дефект закрепляется как стандарт де-факто. → Твёрдая черта: несоответствие типа = отказ. Терпимость сюда = проглатывание мусора.

51Вид 4 — лишний ЭЛЕМЕНТ в списке. Два подслучая:

  • 52(а) Список длиннее, чем ждёт читатель (аппаратный лимит у потребителя). Это авария Cloudflare [ИЗ СВОДА]: сгенерированный файл вырос вдвое, у развёрнутых потребителей был зашит предел → глобальный сбой. Урок: читатели не должны хардкодить потолок длины открытых списков. → Терпим длину (читаем все).
  • (б) Элемент неожиданного ТИПА в однородном списке (строка в Vec<u64>). То же, что Вид 3. → Отказ.

53Вид 5 — незнакомый КЛЮЧ в закрытой карте. Для BTreeMap<String, X> незнакомые ключи обычно безопасны (карты открыты по природе). Но если значения типизированы/enum — к значениям применяется правило Вида 3. Наш features: BTreeMap<String, Vec<String>> (content.rs:39) открыт по ключам и строковый по значениям → полностью терпим.

Где терпимость перестаёт быть эволюцией

54В тот момент, когда она отображает нераспознанный вход в определённое значение, на котором потребитель потом действует (паттерн Cloudflare «score zero»). Молчаливый default неизвестного значения enum — единственная самая опасная терпимость. Добавление (новые поля, новая длина, новые ключи карты) — терпимо; нарушение типа — нет; новые значения enum — не молча в default.

Уточнение к рекомендациям свода

55«Строго на двери, терпимо дальше» — верно, но недоспецифицировано на enum'ах. Я бы уточнил: дверь (vibe.toml) отвергает и неизвестные значения enum, и опечатки; каталог (вниз по потоку) терпит незнакомые поля, но всё равно не молча дефолтит неизвестные значения enum — несёт их как Unknown, чтобы будущий читатель мог ими воспользоваться. Это более острая граница, чем просто «терпимо дальше».

5. Что из механики protobuf нам НЕ подходит — и наследуем ли мы проблемы JSON

Что НЕ переносится

  • 56Числа как идентичность поля — ядро protobuf. См. §6: у нас имена. Мы не наследуем их rename-безопасность, но она нам и не нужна при правильной дисциплине имён.
  • Двоичный формат — нерелевантен: мы публикуем JSON для читаемости людьми и инструментами, компактность не покупаем.
  • Драма с удалением required [ИЗ СВОДА]: protobuf убрал required из-за долго живущих типов через границы организаций. У нас один писатель и ноль внешних потребителей [ИЗ СВОДА] — обязательные поля (VersionEntry полон них, entry/mod.rs:43-121) нормальны и полезны. Урок «required — зло» нельзя тащить оптом: он настроен на их развёртывание (много организаций, типы на десятилетия). Я бы локализовал: required зол лишь когда читатели, которых ты не контролируешь, могут быть вынуждены синтезировать значение. Писателя контролируем мы. Другой случай.
  • Коллапс присутствия скаляров в proto3 [ИЗ СВОДА]: proto3 не различает «нет» и default для скаляров. В JSON этого нет: отсутствие ключа, 0 и null — три разных состояния. Так что скалярного коллапса proto3 мы вообще не наследуем.

Наследуем ли мы «проблемы JSON-кодировки без выхода» — НЕ согласен с рамкой

57Свод [ИЗ СВОДА]: JSON-кодек protobuf по умолчанию отвергает незнакомые поля, а на жалобы ответ — «используйте двоичную»; мы наследуем весь набор проблем их JSON без их выхода.

58Я не согласен с рамкой. «JSON-проблема» protobuf в том, что их JSON строг, а двоичный — терпим, и недовольных отсылают в двоичный. Нас никто не заставляет быть строгими в JSON: serde позволяет выбрать deny_unknown_fields или нет. Сейчас мы сами выбрали строгость в 15 местах своего читателя и терпимость — в клиенте. Так что «выход» (терпимое чтение) тривиально доступен нам убиранием одного атрибута — двоичный формат для выхода не нужен. Свод подаёт так, будто двоичный protobuf — единственный выход из строгого JSON; для нас выход — терпимый JSON, и он бесплатен.

59Что мы реально наследуем от JSON-без-схемы: не «проблемы protobuf», а проще — в JSON нет схемы, поэтому дисциплина эволюции живёт целиком в головах + чекере. Это и есть урок Cloudflare [ИЗ СВОДА]: ужесточить приём собственных сгенерированных файлов так же, как пользовательский ввод. И — повторю из §1 — артефакт схемы для каталога у нас отсутствует, хотя способность его публиковать в доме есть (vibe-cli/resources/package-tree.schema.v1.json).

6. Номера полей против имён — что теряем и возмещаемо ли

60У protobuf идентичность поля — число; у нас — имя (JSON/TOML ключ). Урок protobuf [ПО ПАМЯТИ, НЕ ПРОВЕРЕНО]: переименование Rust-поля безопасно (число остаётся), переименование НЕ является изменением провода.

61Что мы теряем, используя имена:

  • 62Rename-безопасность. У нас переименование поля (authorauthors) — это изменение провода: старые читатели ищут «author» и не находят «authors».
  • Имена утекают в идентичность: на «более удачное имя» возникает ломающее искушение, которого числа бы не дали (обратная сторона: имена самодокументируемы).

63Возмещаемо ли в JSON и TOML — да:

  • 64Список отставленных имён (рекомендация свода №6) — необходим, но недостаточен: он предотвращает коллизию, но не даёт rename-безопасности, лишь делает переименования явно-ломающими-и-отслеживаемыми.
  • Алиасы при десериализации — настоящий механизм, которого свод не назвал:
65  #[serde(alias = "author")]
  pub authors: Vec<String>,

66читатель принимает и «author», и «authors», обратно пишет «authors». Это и есть rename-безопасность, дёшево, в чистом JSON. Цена: алиас живёт в читателе (или пока все старые данные не мигрированы); писатель выпускает только новое имя; round-trip нормализует. Для каталога, переписываемого при каждом индексировании, миграция автоматична (следующая сборка пишет новые имена).

  • 67В TOML алиас работает так же — мы читаем через toml::from_str (как в crates/vibe-index/src/lockfile.rs:48), serde-атрибуты едины для форматов. Так что компенсация одинакова для JSON и TOML; специфика TOML — не идентичность поля (ключи — те же строки), а отсутствие null (см. §2).

68Вердикт: мы теряем rename-безопасность и непрозрачную идентичность; полностью возмещаемо через #[serde(alias)] + список отставленных имён + автоматическое переиндексирование. Числа нам не нужны. Числа побеждают только в высокочурном сетевом протоколе со многими независимыми командами; мы — читаемый человеком формат данных в покое, здесь имена + алиасы лучше.

С чем я согласен — и что добавляю

  • 69Терпимый по умолчанию каталог прав (клиент уже таков). Добавляю: каталог прямо-совместим ровно благодаря терпимому читателю — поэтому собственный строгий читатель сервера (15 deny_unknown_fields) — аномалия, и именно она делает так, что новый сервер не запустится на каталоге, записанном ещё более новым (repomd.rs:15 + index/repomd.rs:38).
  • Строго на двери, терпимо дальше — согласен; уточнил границу по enum'ам (§4: reject или Unknown, никогда молча в default).
  • Контракт версии (старшая — отказ, младшая — предупреждение, отсутствие — 1.0) — согласен. Добавляю: сегодня версия каталога не сравнивается ни разу (index/repomd.rs:32-39), то есть у нас ярлык версии без контракта; пробел — в отсутствии самого сравнения, а не в форме контракта.
  • Каждое объединение — тегированное — согласен (§3, Форма A).
  • Не переиспользовать имена, держать список отставленных — согласен; добавил алиасы как недостающий механизм rename-безопасности (§6).

Итоговый вердикт

70Из шести пунктов: в трёх (Avro, списки-присутствие, JSON-наследство protobuf) я меняю вывод свода; в одном (объединение) поправляю факт и предлагаю пять форм; в двух (строгость, номера полей) уточняю и добавляю. Ни одного пункта не принял без добавления. Центральная конструктивная находка поверх свода: способность публиковать машинно-читаемый артефакт схемы и версионировать его по пути уже существует в этом репозитории (package-tree.schema.v1.json + jsonschema + золотой тест) — просто не применена к каталогу; многие «теоретические» ответы свода здесь уже имеют рабочий образец для копирования.

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/research/schema-evolution-2026-08/07-glm-format-mechanics

.md.xmlllms.txt