<?xml version="1.0" encoding="UTF-8"?>
<spec xmlns="https://vibevm.org/spec/1">
  <title>МЕГАОТЧЁТ — эволюция форматов vibevm</title>
  <p p="1">_Сведён 2026-08-09. Вход: один замер дерева, четыре веб-исследования, четыре
независимых разбора GLM. Всё сырьё — файлы 01–09 в этом каталоге._</p>
  <section title="0. Как читать этот документ">
    <p p="2">Раздел 5 — главный. Там разобраны шесть противоречий, и три из них — ошибки в
моём собственном своде, найденные воркерами. Разделы 1–4 — материал, раздел 6 —
предложение, раздел 7 — то, что решает владелец, раздел 8 — задание ревьюеру.</p>
    <p p="3">Разметка источника обязательна и сохранена: `[ИЗМЕРЕНО]` — чтение нашего дерева
с `файл:строка`; `[ВЕБ]` — веб-исследование с дословной цитатой и датой
обращения; `[GLM]` — суждение воркера без доступа в сеть.</p>
  </section>
  <section title="1. Вопрос и как он изменился">
    <p p="4">**Начали с:** каталог пакетов публикуется как JSON в git-репозитории; решено
описать его схемами и генерировать код; где провести границу, если одна форма в
языке схем невыразима.</p>
    <p p="5">**Пришли к:** граница генерации — наименее интересная часть. Настоящий вопрос —
**какова политика эволюции долговечных форматов vibevm**, потому что форматов
три, решены они по-разному, и никто этого не решал.</p>
    <p p="6">**Владелец добавил** третий поворот: те же выводы применить к парсеру
`vibe.toml`. Это превращает вопрос из «дизайн одного файла» в «один контракт на
три файла».</p>
  </section>
  <section title="2. Источники">
    <table p="7">
      <tr>
        <td>файл</td>
        <td>что это</td>
        <td>чем получено</td>
      </tr>
      <tr>
        <td>`01-measure-our-wire-format-claudez.xml`</td>
        <td>23 типа, 86 полей, все объединения, все словари, кругооборот</td>
        <td>GLM по дереву</td>
      </tr>
      <tr>
        <td>`02-research-package-indexes-web.xml`</td>
        <td>crates.io, OCI, Debian, Go, npm, Maven, RPM, PyPI, NuGet, Homebrew</td>
        <td>веб, дословные цитаты</td>
      </tr>
      <tr>
        <td>`03-research-serialization-mechanics-web.xml`</td>
        <td>протобуф, Avro, Thrift, JTD, Cap'n Proto, JSON Schema, Kafka</td>
        <td>веб, дословные цитаты</td>
      </tr>
      <tr>
        <td>`04-research-client-survival-web.xml`</td>
        <td>Meta, Twitter, Google, LinkedIn, Badoo, GraphQL, Discord, Cloudflare</td>
        <td>веб, дословные цитаты</td>
      </tr>
      <tr>
        <td>`05-boss-findings-digest.xml`</td>
        <td>мой свод — **вход для GLM, содержит три ошибки, см. §5**</td>
        <td>я</td>
      </tr>
      <tr>
        <td>`06-glm-neighbours-and-principles.xml`</td>
        <td>принцип за приёмами; отличия нашего случая</td>
        <td>GLM</td>
      </tr>
      <tr>
        <td>`07-glm-format-mechanics.xml`</td>
        <td>пять форм тега; скепсис к Avro; граница терпимости</td>
        <td>GLM</td>
      </tr>
      <tr>
        <td>`08-glm-clients-and-vocabularies.xml`</td>
        <td>формы запасного значения; **где наш читатель поймёт неверно**</td>
        <td>GLM</td>
      </tr>
      <tr>
        <td>`09-glm-manifest-and-unified-policy.xml`</td>
        <td>манифест; довод против единой политики; чекер</td>
        <td>GLM</td>
      </tr>
    </table>
  </section>
  <section title="3. Наше состояние — измерено, расхождения разрешены">
    <p p="8">Три долговечных формата, три разных ответа, ни один не выбран сознательно:</p>
    <table p="9">
      <tr>
        <td></td>
        <td>версия в данных</td>
        <td>кто-то ветвится</td>
        <td>незнакомый ключ</td>
      </tr>
      <tr>
        <td>`vibe.lock`</td>
        <td>есть (5)</td>
        <td>**да, отвергает**</td>
        <td>отвергается</td>
      </tr>
      <tr>
        <td>каталог индекса</td>
        <td>есть (1), на КАЖДОЙ записи</td>
        <td>**нет, никто**</td>
        <td>отвергается</td>
      </tr>
      <tr>
        <td>`vibe.toml`</td>
        <td>**нет вовсе**</td>
        <td>—</td>
        <td>отвергается</td>
      </tr>
    </table>
    <p p="10">Каталог `[ИЗМЕРЕНО]`:</p>
    <list ordered="false" p="11">
      <item>**23 типа** на проводе; 18 — файлы каталога (17 структур + 1 объединение).</item>
      <item>**86 полей**; из них **21** таково, что «пусто» неотличимо от «нет поля»
  (14 коллекций + 7 вложенных структур — см. §5.2).</item>
      <item>**1 объединение**, и оно **полутегированное**, а не нетегированное (§5.1).</item>
      <item>**`deny_unknown_fields` в 15 местах.** Каталог перечитывается ради перезаписи
  на **6 путях**. Незнакомое поле не теряет данные — оно **не даёт прочитать
  каталог**; сервер грузит его на старте, значит старый сервер **не запустится**
  на каталоге, записанном новым.</item>
      <item>**5 закрытых словарей** в крейте (4 в `types/`, см. §5.3), у всех неизвестное
  значение — ошибка разбора. `#[serde(other)]` нет нигде.</item>
      <item>**Версия — ярлык, а не переключатель:** ни одного сравнения.</item>
      <item>Спека обещает терпимость к незнакомым полям, помечено «реализовано». Код
  делает противоположное. `[GLM]` добавляет: **спека противоречит и самой
  себе** — два взаимоисключающих утверждения, оба «реализовано», а код делает
  третье. Я видела одну трещину из трёх.</item>
    </list>
    <p p="12">**Четыре места, где старый читатель разберёт успешно и поймёт неверно** `[GLM]`
— это отказ вида Cloudflare, найденный у нас: `repomd.rs:42-55`,
`memory.rs:262-280`, `inverted.rs:66,75`, `subskill.rs:144-146`. Подробности —
файл 08. **Это самая ценная находка всей серии.**</p>
  </section>
  <section title="4. Что сошлось у зрелых систем">
    <p p="13">Тринадцать сходимостей в файле 02; здесь несущие.</p>
    <list ordered="true" p="14">
      <item>**«Не ломать, только добавлять» — универсально**, и это дисциплина ПИСАТЕЛЯ.
   Следствие: формат вечно копит необязательные поля, и **сузить тип после
   публикации нельзя никогда**.</item>
      <item>**Добавление поля НЕ поднимает версию.** Версию поднимает только то, из-за
   чего старый читатель **неверно поймёт уже существующие данные**.</item>
      <item>**Нетегированные объединения — самый надёжный источник сожалений**, и
   расплата всегда одна: **новое поле**, ни разу — починка на месте.</item>
      <item>**Незнакомые поля игнорируют на практике, но почти никто не пишет этого
   нормативно.** «Снисходительность, которую вы не записали, — это
   снисходительность, которой у вас нет.»</item>
      <item>**«Отсутствие» у всех означает значение по умолчанию; «не записано» не
   моделирует почти никто**, и внедрить это задним числом **невозможно, потому
   что отсутствие уже что-то означает**.</item>
      <item>**Оба края останавливают эволюцию.** Maven замёрз от строгости; RPM — от
   отсутствия версии. Работает **только** терпимый читатель **плюс** сигнал
   версии.</item>
      <item>**Сигнал версии вешают на наименьшую единицу** — crates.io версионирует
   запись, не файл. У нас так уже сделано.</item>
      <item>**Ломающее изменение — новый ПУТЬ**, а не новая старшая версия в том же
   документе. Независимо: PyPI, NuGet.</item>
      <item>**Контракт — строка-идентификатор, а не то, что она обозначает.**
   Единственный смертельный слом RPM за двадцать лет — переопределение смысла
   существующего тега, не добавление новых.</item>
      <item>**Авторский документ ≠ опубликованный документ.** Независимо: Maven, npm,
    crates.io, Debian. **Прямой ответ на вопрос владельца про манифест.**</item>
      <item>**Версионный шлагбаум защищает только тех читателей, у кого он уже есть.**
    Cargo отгрузил поле версии недокументированным за годы до нужды. Go
    вывернулся, бэкпортировав проверку, — что мог себе позволить только потому,
    что выпускает один инструмент.</item>
      <item>**Версия в заголовке протокола отвергнута именно из-за нашего случая** —
    чтобы зеркала могли оставаться тупым файловым сервером.</item>
      <item>**Запасное значение в словарях нельзя внедрить задним числом** — незнакомое
    значение обязано быть положено в ячейку, перечисляющую только известное.</item>
    </list>
  </section>
  <section title="5. Противоречия и их разрешение">
    <section title="5.1 Моё описание объединения было неверно — нашли трое независимо">
      <p p="15">Я писала: «общего поля-признака нет, читатель угадывает по набору ключей».</p>
      <p p="16">**Проверено мной по дереву** (`types/repomd.rs:41-54`): вариант `Directory`
**несёт** `kind: DirectoryTag`, и доккомментарий прямо говорит, что тег стоит
там **намеренно**, чтобы сопоставитель различал однозначно. Тега нет у варианта
`File`.</p>
      <p p="17">**Разрешение.** Объединение **полутегированное**, а не нетегированное. Это
меняет две вещи:</p>
      <list ordered="false" p="18">
        <item>Аналогия с аварией PyPI **структурно натянута** `[GLM ×2]`: там сломался
  разбор значения переменного ТИПА (строка против словаря), у нас оба варианта —
  объекты, и один помечен.</item>
        <item>**Починка дешевле, чем я говорила**: перейти с `untagged` на явный тег — по
  сути один атрибут, и **именно это разблокирует исходный вопрос**, потому что
  тип становится выразимым в языке схем ровно в тот момент, когда мы перестаём
  брать `untagged` `[GLM]`.</item>
      </list>
      <p p="19">Пять конкретных форм с ценой каждой — файл 07.</p>
    </section>
    <section title="5.2 «14 полей» против «21»">
      <p p="20">Замер: 21 поле с пропуском-когда-пусто, из них 14 коллекций, 7 вложенных
структур. Мой свод взял 14.</p>
      <p p="21">**Разрешение: оба числа верны, вопросы разные.** Коллекций — 14. **Полей, где
«пусто» неотличимо от «нет поля», — 21**, потому что вложенные структуры
схлопываются точно так же `[GLM]`. Решение принимается по **21**, и мой свод
занизил вход.</p>
    </section>
    <section title="5.3 «5 словарей» против «4» и «3»">
      <p p="22">Замер — 5, один воркер — 4, другой — 3.</p>
      <p p="23">**Разрешение: расхождение периметра, не факта.** 4 живут в `types/`, пятый
(`BindingSite`) — в `index/inverted.rs`. Кто считал только `types/`, получил 4;
кто считал только те, что реально едут в файлы каталога, — 3.</p>
      <p p="24">Это наш собственный стоячий закон: **число без названного периметра —
это число, которое следующий читатель выведет неправильно.** Я его нарушила
первой.</p>
    </section>
    <section title="5.4 Противоречие внутри самого корпуса: когда поднимать версию">
      <p p="25">crates.io добавляет поля **без** поднятия. PyPI **требует** поднимать младшую.
Обе политики в исследовании одобрены. Это настоящая нестыковка.</p>
      <p p="26">**Разрешение: они отвечают на разные вопросы, потому что версия у них служит
разному.**</p>
      <fence p="27">Версия как ШЛАГБАУМ (crates.io)
  смысл: «сможешь ли ты вообще интерпретировать эту запись»
  правило: старше моей — пропусти ЗАПИСЬ, остальное читай
  добавление поля этого не меняет  →  НЕ поднимаем

Версия как ДЕКЛАРАЦИЯ ВОЗМОЖНОСТЕЙ (PyPI)
  смысл: «какие поля ты вправе здесь ожидать»
  правило: клиент смотрит версию, чтобы знать, что будет
  чтобы это работало, поля версии должны быть ОБЯЗАТЕЛЬНЫ  →  поднимаем</fence>
      <p p="28">Одним целым числом обе роли не сыграть. **Сначала решаем, ЧЕМ у нас служит
версия, и только потом — когда её двигать.**</p>
      <p p="29">**Для каталога шлагбаум сильно лучше**, и мы к нему уже ближе: версия у нас
стоит на **каждой записи**. Каталог — это множество независимых записей;
пропустить одну непонятную запись — деградация, отвергнуть весь файл — отказ.</p>
    </section>
    <section title="5.5 Avro-в-git — отзываю">
      <p p="30">Я говорила: схема писателя рядом с данными, а у нас схема и данные в одной
истории git, значит почти бесплатно.</p>
      <p p="31">**Два воркера независимо это разобрали, и они правы.** Каталог и схемы — в
**разных** репозиториях и историях; внешний читатель без доступа к нашему репо
модель не выполнит; запись, скопированная наружу, теряет связь со схемой. И
главное — **это противоречит моему же критерию**: «свойство отношений умирает
при соприкосновении с файлом», а «сходи забери мою схему» есть отношение.</p>
      <p p="32">**Отзываю. Терпимый читатель дешевле, и он у нас уже написан.**</p>
    </section>
    <section title="5.6 Стратегическая поправка: механизмы против обязательств">
      <p p="33">Мой свод предлагал принять дисциплину целиком сейчас. Возражение `[GLM]`:
при нуле читателей дешевле **ломать и переименовывать**, а преждевременная
заморозка продаёт наше единственное преимущество перед зрелыми соседями.</p>
      <p p="34">**Разрешение — разделить два разных вида решений:**</p>
      <fence p="35">МЕХАНИЗМЫ — обязаны существовать ДО первого читателя,
            потому что задним числом не внедряются:
  · тег на объединении
  · запасное значение в словарях
  · терпимый читатель
  · версия с контрактом
  · решённое значение «отсутствия»

ОБЯЗАТЕЛЬСТВА — начинаются В МОМЕНТ ПУБЛИКАЦИИ, не сейчас:
  · никогда не переименовывать
  · вечные псевдонимы
  · список отставленных имён
  · сроки устаревания</fence>
      <p p="36">Пока читателей ноль, обязательства стоят свободы и не покупают ничего.
Механизмы — наоборот: **их окно закрывается вместе с публикацией.**</p>
    </section>
    <section title="5.7 Поправка к «строгость зависит от автора файла»">
      <p p="37">Я предлагала: рукописный файл — строго, машинный — терпимо.</p>
      <p p="38">**Возражение `[GLM]`: из файла нельзя узнать, кто его написал.** Я сама задала
этот вопрос воркеру и получила ответ «никак».</p>
      <p p="39">**Разрешение:** критерий не «кто написал», а **какой это файл** (это известно
всегда) и **в каком пространстве имён ключ** (это выразимо). Формулировка
переписывается:</p>
      <fence p="40">Файл, который редактирует человек   → незнакомый ключ в НАШЕМ
(vibe.toml)                            пространстве = ошибка (ловим опечатку)
                                     → чужое пространство = не наше дело

Файл, который пишет машина          → незнакомое поле = игнорировать
(каталог)                              (переживаем будущее)</fence>
    </section>
  </section>
  <section title="6. Предложение">
    <p p="41">**Механизмы — до первой публикации, потому что окно закроется.**</p>
    <list ordered="true" p="42">
      <item>**Объединение делается симметрично тегированным.** Переход с `untagged` на
   явный тег; вариант `File` получает свой тег. Разблокирует исходный вопрос про
   генерацию: тип становится выразимым в языке схем.</item>
      <item>**Каждый закрытый словарь получает запасное значение** с сохранением исходной
   строки. Пять словарей. Без этого добавление седьмого вида пакета — ломающее
   изменение для каждого внешнего читателя, навсегда.</item>
      <item>**Читатель каталога становится терпимым к незнакомым ПОЛЯМ** — снять
   `deny_unknown_fields` с типов каталога. Это один атрибут, и он же делает
   правдой обещание, которое спека уже даёт. Писатель остаётся строгим.</item>
      <item>**Отсутствие получает решённый смысл, по одному правилу на оба формата:**
   отсутствие = «не записано»; известное пустое пишется явно пустым списком.
   Решение принимается по **21** полю, не по 14. `null` не нужен — его нет в
   TOML, и правило, выживающее в обоих форматах, скорее верное.</item>
      <item>**Версия каталога становится шлагбаумом на уровне записи:** старше моей —
   запись пропускается с сообщением, остальные читаются. Ветвление появляется
   там, где сегодня только присваивание.</item>
      <item>**`vibe.toml` получает версию, и её отсутствие — НЕ «считать первой».**
   Для рукописного формата «версии нет» может значить «стёрли по ошибке».
   Эталон стоит в этом же проекте: `vibe.lock` требует версию и отвергает
   отсутствие.</item>
      <item>**Заповедник для чужих ключей** в манифесте — своя секция, которую vibe не
   трогает и не проверяет.</item>
      <item>**Один чекер на три формата** — набор машинно-проверяемых правил (форма — в
   файле 09). Без него три набора правил разойдутся так же, как уже разошёлся
   словарь видов пакетов, записанный во многих местах.</item>
    </list>
    <p p="43">**Обязательства — с момента первой публикации, не раньше.** До неё ломаем и
переименовываем свободно: это единственное преимущество, которого у зрелых
соседей уже нет.</p>
    <p p="44">**Порядок:** каталог — полигон (ломать даром, здесь строится машинерия);
`vibe.toml` — боевое применение (дорого, но машинерия уже обкатана). Один
воркер оспаривает и этот порядок — разбор в файле 09.</p>
  </section>
  <section title="7. Что остаётся владельцу">
    <list ordered="true" p="45">
      <item>**Чем служит версия каталога — шлагбаумом или декларацией возможностей?**
   §5.4. Рекомендация: шлагбаум на уровне записи.</item>
      <item>**Манифест в опубликованном пакете и манифест проекта — один формат или
   два?** Индустрия независимо пришла к двум (авторский документ ≠
   опубликованный), четыре раза. Один воркер возражает, что `vibe.toml` у нас
   вообще не внешняя поверхность — файл 09.</item>
      <item>**Принимаем ли мы, что отсутствие означает «не записано»**, зная, что у всех
   соседей оно означает «значение по умолчанию» и что переиграть нельзя?</item>
      <item>**Сроки обязательств** — с какого момента формат считается опубликованным и
   ломать больше нельзя.</item>
    </list>
  </section>
  <section title="8. Задание ревьюеру">
    <p p="46">Разобрать этот отчёт и девять исходных файлов; проверить разрешения §5 на
прочность; найти то, что все восемь участников пропустили; вынести суждение по
предложению §6 и по вопросам §7 — что верно, что неверно, чего не хватает.
Особое внимание: §5.4 (роль версии) и §5.6 (механизмы против обязательств) —
это два разрешения, на которых держится всё остальное.</p>
  </section>
</spec>
