МЕГАОТЧЁТ — эволюция форматов vibevm
01Сведён 2026-08-09. Вход: один замер дерева, четыре веб-исследования, четыре независимых разбора GLM. Всё сырьё — файлы 01–09 в этом каталоге.
0. Как читать этот документ
02Раздел 5 — главный. Там разобраны шесть противоречий, и три из них — ошибки в моём собственном своде, найденные воркерами. Разделы 1–4 — материал, раздел 6 — предложение, раздел 7 — то, что решает владелец, раздел 8 — задание ревьюеру.
03Разметка источника обязательна и сохранена: [ИЗМЕРЕНО] — чтение нашего дерева
с файл:строка; [ВЕБ] — веб-исследование с дословной цитатой и датой
обращения; [GLM] — суждение воркера без доступа в сеть.
1. Вопрос и как он изменился
04Начали с: каталог пакетов публикуется как JSON в git-репозитории; решено описать его схемами и генерировать код; где провести границу, если одна форма в языке схем невыразима.
05Пришли к: граница генерации — наименее интересная часть. Настоящий вопрос — какова политика эволюции долговечных форматов vibevm, потому что форматов три, решены они по-разному, и никто этого не решал.
06Владелец добавил третий поворот: те же выводы применить к парсеру
vibe.toml. Это превращает вопрос из «дизайн одного файла» в «один контракт на
три файла».
2. Источники
| файл | что это | чем получено |
|---|---|---|
01-measure-our-wire-format-claudez.xml |
23 типа, 86 полей, все объединения, все словари, кругооборот | GLM по дереву |
02-research-package-indexes-web.xml |
crates.io, OCI, Debian, Go, npm, Maven, RPM, PyPI, NuGet, Homebrew | веб, дословные цитаты |
03-research-serialization-mechanics-web.xml |
протобуф, Avro, Thrift, JTD, Cap'n Proto, JSON Schema, Kafka | веб, дословные цитаты |
04-research-client-survival-web.xml |
Meta, Twitter, Google, LinkedIn, Badoo, GraphQL, Discord, Cloudflare | веб, дословные цитаты |
05-boss-findings-digest.xml |
мой свод — вход для GLM, содержит три ошибки, см. §5 | я |
06-glm-neighbours-and-principles.xml |
принцип за приёмами; отличия нашего случая | GLM |
07-glm-format-mechanics.xml |
пять форм тега; скепсис к Avro; граница терпимости | GLM |
08-glm-clients-and-vocabularies.xml |
формы запасного значения; где наш читатель поймёт неверно | GLM |
09-glm-manifest-and-unified-policy.xml |
манифест; довод против единой политики; чекер | GLM |
3. Наше состояние — измерено, расхождения разрешены
08Три долговечных формата, три разных ответа, ни один не выбран сознательно:
| версия в данных | кто-то ветвится | незнакомый ключ | |
|---|---|---|---|
vibe.lock |
есть (5) | да, отвергает | отвергается |
| каталог индекса | есть (1), на КАЖДОЙ записи | нет, никто | отвергается |
vibe.toml |
нет вовсе | — | отвергается |
10Каталог [ИЗМЕРЕНО]:
- 1123 типа на проводе; 18 — файлы каталога (17 структур + 1 объединение).
- 86 полей; из них 21 таково, что «пусто» неотличимо от «нет поля» (14 коллекций + 7 вложенных структур — см. §5.2).
- 1 объединение, и оно полутегированное, а не нетегированное (§5.1).
deny_unknown_fieldsв 15 местах. Каталог перечитывается ради перезаписи на 6 путях. Незнакомое поле не теряет данные — оно не даёт прочитать каталог; сервер грузит его на старте, значит старый сервер не запустится на каталоге, записанном новым.- 5 закрытых словарей в крейте (4 в
types/, см. §5.3), у всех неизвестное значение — ошибка разбора.#[serde(other)]нет нигде. - Версия — ярлык, а не переключатель: ни одного сравнения.
- Спека обещает терпимость к незнакомым полям, помечено «реализовано». Код
делает противоположное.
[GLM]добавляет: спека противоречит и самой себе — два взаимоисключающих утверждения, оба «реализовано», а код делает третье. Я видела одну трещину из трёх.
12Четыре места, где старый читатель разберёт успешно и поймёт неверно [GLM]
— это отказ вида Cloudflare, найденный у нас: repomd.rs:42-55,
memory.rs:262-280, inverted.rs:66,75, subskill.rs:144-146. Подробности —
файл 08. Это самая ценная находка всей серии.
4. Что сошлось у зрелых систем
13Тринадцать сходимостей в файле 02; здесь несущие.
- 14«Не ломать, только добавлять» — универсально, и это дисциплина ПИСАТЕЛЯ. Следствие: формат вечно копит необязательные поля, и сузить тип после публикации нельзя никогда.
- Добавление поля НЕ поднимает версию. Версию поднимает только то, из-за чего старый читатель неверно поймёт уже существующие данные.
- Нетегированные объединения — самый надёжный источник сожалений, и расплата всегда одна: новое поле, ни разу — починка на месте.
- Незнакомые поля игнорируют на практике, но почти никто не пишет этого нормативно. «Снисходительность, которую вы не записали, — это снисходительность, которой у вас нет.»
- «Отсутствие» у всех означает значение по умолчанию; «не записано» не моделирует почти никто, и внедрить это задним числом невозможно, потому что отсутствие уже что-то означает.
- Оба края останавливают эволюцию. Maven замёрз от строгости; RPM — от отсутствия версии. Работает только терпимый читатель плюс сигнал версии.
- Сигнал версии вешают на наименьшую единицу — crates.io версионирует запись, не файл. У нас так уже сделано.
- Ломающее изменение — новый ПУТЬ, а не новая старшая версия в том же документе. Независимо: PyPI, NuGet.
- Контракт — строка-идентификатор, а не то, что она обозначает. Единственный смертельный слом RPM за двадцать лет — переопределение смысла существующего тега, не добавление новых.
- Авторский документ ≠ опубликованный документ. Независимо: Maven, npm, crates.io, Debian. Прямой ответ на вопрос владельца про манифест.
- Версионный шлагбаум защищает только тех читателей, у кого он уже есть. Cargo отгрузил поле версии недокументированным за годы до нужды. Go вывернулся, бэкпортировав проверку, — что мог себе позволить только потому, что выпускает один инструмент.
- Версия в заголовке протокола отвергнута именно из-за нашего случая — чтобы зеркала могли оставаться тупым файловым сервером.
- Запасное значение в словарях нельзя внедрить задним числом — незнакомое значение обязано быть положено в ячейку, перечисляющую только известное.
5. Противоречия и их разрешение
5.1 Моё описание объединения было неверно — нашли трое независимо
15Я писала: «общего поля-признака нет, читатель угадывает по набору ключей».
16Проверено мной по дереву (types/repomd.rs:41-54): вариант Directory
несёт kind: DirectoryTag, и доккомментарий прямо говорит, что тег стоит
там намеренно, чтобы сопоставитель различал однозначно. Тега нет у варианта
File.
17Разрешение. Объединение полутегированное, а не нетегированное. Это меняет две вещи:
- 18Аналогия с аварией PyPI структурно натянута
[GLM ×2]: там сломался разбор значения переменного ТИПА (строка против словаря), у нас оба варианта — объекты, и один помечен. - Починка дешевле, чем я говорила: перейти с
untaggedна явный тег — по сути один атрибут, и именно это разблокирует исходный вопрос, потому что тип становится выразимым в языке схем ровно в тот момент, когда мы перестаём братьuntagged[GLM].
19Пять конкретных форм с ценой каждой — файл 07.
5.2 «14 полей» против «21»
20Замер: 21 поле с пропуском-когда-пусто, из них 14 коллекций, 7 вложенных структур. Мой свод взял 14.
21Разрешение: оба числа верны, вопросы разные. Коллекций — 14. Полей, где
«пусто» неотличимо от «нет поля», — 21, потому что вложенные структуры
схлопываются точно так же [GLM]. Решение принимается по 21, и мой свод
занизил вход.
5.3 «5 словарей» против «4» и «3»
22Замер — 5, один воркер — 4, другой — 3.
23Разрешение: расхождение периметра, не факта. 4 живут в types/, пятый
(BindingSite) — в index/inverted.rs. Кто считал только types/, получил 4;
кто считал только те, что реально едут в файлы каталога, — 3.
24Это наш собственный стоячий закон: число без названного периметра — это число, которое следующий читатель выведет неправильно. Я его нарушила первой.
5.4 Противоречие внутри самого корпуса: когда поднимать версию
25crates.io добавляет поля без поднятия. PyPI требует поднимать младшую. Обе политики в исследовании одобрены. Это настоящая нестыковка.
26Разрешение: они отвечают на разные вопросы, потому что версия у них служит разному.
27Версия как ШЛАГБАУМ (crates.io)
смысл: «сможешь ли ты вообще интерпретировать эту запись»
правило: старше моей — пропусти ЗАПИСЬ, остальное читай
добавление поля этого не меняет → НЕ поднимаем
Версия как ДЕКЛАРАЦИЯ ВОЗМОЖНОСТЕЙ (PyPI)
смысл: «какие поля ты вправе здесь ожидать»
правило: клиент смотрит версию, чтобы знать, что будет
чтобы это работало, поля версии должны быть ОБЯЗАТЕЛЬНЫ → поднимаем
28Одним целым числом обе роли не сыграть. Сначала решаем, ЧЕМ у нас служит версия, и только потом — когда её двигать.
29Для каталога шлагбаум сильно лучше, и мы к нему уже ближе: версия у нас стоит на каждой записи. Каталог — это множество независимых записей; пропустить одну непонятную запись — деградация, отвергнуть весь файл — отказ.
5.5 Avro-в-git — отзываю
30Я говорила: схема писателя рядом с данными, а у нас схема и данные в одной истории git, значит почти бесплатно.
31Два воркера независимо это разобрали, и они правы. Каталог и схемы — в разных репозиториях и историях; внешний читатель без доступа к нашему репо модель не выполнит; запись, скопированная наружу, теряет связь со схемой. И главное — это противоречит моему же критерию: «свойство отношений умирает при соприкосновении с файлом», а «сходи забери мою схему» есть отношение.
32Отзываю. Терпимый читатель дешевле, и он у нас уже написан.
5.6 Стратегическая поправка: механизмы против обязательств
33Мой свод предлагал принять дисциплину целиком сейчас. Возражение [GLM]:
при нуле читателей дешевле ломать и переименовывать, а преждевременная
заморозка продаёт наше единственное преимущество перед зрелыми соседями.
34Разрешение — разделить два разных вида решений:
35МЕХАНИЗМЫ — обязаны существовать ДО первого читателя,
потому что задним числом не внедряются:
· тег на объединении
· запасное значение в словарях
· терпимый читатель
· версия с контрактом
· решённое значение «отсутствия»
ОБЯЗАТЕЛЬСТВА — начинаются В МОМЕНТ ПУБЛИКАЦИИ, не сейчас:
· никогда не переименовывать
· вечные псевдонимы
· список отставленных имён
· сроки устаревания
36Пока читателей ноль, обязательства стоят свободы и не покупают ничего. Механизмы — наоборот: их окно закрывается вместе с публикацией.
5.7 Поправка к «строгость зависит от автора файла»
37Я предлагала: рукописный файл — строго, машинный — терпимо.
38Возражение [GLM]: из файла нельзя узнать, кто его написал. Я сама задала
этот вопрос воркеру и получила ответ «никак».
39Разрешение: критерий не «кто написал», а какой это файл (это известно всегда) и в каком пространстве имён ключ (это выразимо). Формулировка переписывается:
40Файл, который редактирует человек → незнакомый ключ в НАШЕМ
(vibe.toml) пространстве = ошибка (ловим опечатку)
→ чужое пространство = не наше дело
Файл, который пишет машина → незнакомое поле = игнорировать
(каталог) (переживаем будущее)
6. Предложение
41Механизмы — до первой публикации, потому что окно закроется.
- 42Объединение делается симметрично тегированным. Переход с
untaggedна явный тег; вариантFileполучает свой тег. Разблокирует исходный вопрос про генерацию: тип становится выразимым в языке схем. - Каждый закрытый словарь получает запасное значение с сохранением исходной строки. Пять словарей. Без этого добавление седьмого вида пакета — ломающее изменение для каждого внешнего читателя, навсегда.
- Читатель каталога становится терпимым к незнакомым ПОЛЯМ — снять
deny_unknown_fieldsс типов каталога. Это один атрибут, и он же делает правдой обещание, которое спека уже даёт. Писатель остаётся строгим. - Отсутствие получает решённый смысл, по одному правилу на оба формата:
отсутствие = «не записано»; известное пустое пишется явно пустым списком.
Решение принимается по 21 полю, не по 14.
nullне нужен — его нет в TOML, и правило, выживающее в обоих форматах, скорее верное. - Версия каталога становится шлагбаумом на уровне записи: старше моей — запись пропускается с сообщением, остальные читаются. Ветвление появляется там, где сегодня только присваивание.
vibe.tomlполучает версию, и её отсутствие — НЕ «считать первой». Для рукописного формата «версии нет» может значить «стёрли по ошибке». Эталон стоит в этом же проекте:vibe.lockтребует версию и отвергает отсутствие.- Заповедник для чужих ключей в манифесте — своя секция, которую vibe не трогает и не проверяет.
- Один чекер на три формата — набор машинно-проверяемых правил (форма — в файле 09). Без него три набора правил разойдутся так же, как уже разошёлся словарь видов пакетов, записанный во многих местах.
43Обязательства — с момента первой публикации, не раньше. До неё ломаем и переименовываем свободно: это единственное преимущество, которого у зрелых соседей уже нет.
44Порядок: каталог — полигон (ломать даром, здесь строится машинерия);
vibe.toml — боевое применение (дорого, но машинерия уже обкатана). Один
воркер оспаривает и этот порядок — разбор в файле 09.
7. Что остаётся владельцу
- 45Чем служит версия каталога — шлагбаумом или декларацией возможностей? §5.4. Рекомендация: шлагбаум на уровне записи.
- Манифест в опубликованном пакете и манифест проекта — один формат или
два? Индустрия независимо пришла к двум (авторский документ ≠
опубликованный), четыре раза. Один воркер возражает, что
vibe.tomlу нас вообще не внешняя поверхность — файл 09. - Принимаем ли мы, что отсутствие означает «не записано», зная, что у всех соседей оно означает «значение по умолчанию» и что переиграть нельзя?
- Сроки обязательств — с какого момента формат считается опубликованным и ломать больше нельзя.
8. Задание ревьюеру
46Разобрать этот отчёт и девять исходных файлов; проверить разрешения §5 на прочность; найти то, что все восемь участников пропустили; вынести суждение по предложению §6 и по вопросам §7 — что верно, что неверно, чего не хватает. Особое внимание: §5.4 (роль версии) и §5.6 (механизмы против обязательств) — это два разрешения, на которых держится всё остальное.