FOnline Engine
Current master GitHub
Документация Docs/ru/contributing/decisions/0002-public-api-stability-contract.md

ADR-0002: контракт стабильности публичного API

  • Статус: принято
  • Дата: 2026-07-10
  • Изменено: 2026-08-02 после принятия владельцами экспериментального scope-контракта инвентаря native-codegen
  • Владельцы: скрипты, runtime, сборка и выпуск, документация

Контекст

FOnline предоставляет игровым проектам много поверхностей: CMake helpers, команды BuildTools, настройки, структуру пакетов, нативные hooks, методы, типы, события и свойства AngelScript, метаданные, сериализованные сущности, сетевые сообщения, форматы файлов и ABI приложений и runtime.

Доступность не равна обещанию совместимости. До принятия этого решения PUBLIC_API.md смешивал текущие, планируемые и устаревшие утверждения и не мог быть сгенерирован или сверен с исходным кодом. Теперь локализованный индекс публичных контрактов генерируется, фактическая классификация стабильности остаётся во владеющей модели, а корневой путь сохраняется как устойчивый legacy-маршрут. Замораживание каждого доступного символа также препятствовало бы необходимому рефакторингу движка.

Разработчикам и ИИ-агентам необходимо знать, какая поверхность стабильна, какая экспериментальна, где находится авторитетное объявление и какую работу требует изменение контракта.

Решение

Метки стабильности

Каждый генерируемый публичный контракт использует одну из следующих меток:

Метка Значение
stable Поддерживается для документированных линий выпусков; несовместимые изменения требуют миграции и решения по выпуску.
experimental Публично доступно для проверки, но сигнатура или поведение могут изменяться при наличии явной записи в примечаниях к выпуску.
internal Доступная деталь реализации без обещания совместимости.
deprecated Ранее поддерживаемая поверхность, запланированная к удалению; замена и срок удаления документированы.

Пока символ или поверхность не классифицированы явно, они считаются internal. Существующая доступность, пример в проекте или присутствие в сгенерированных bindings не делают поверхность стабильной автоматически.

Источник истины

  1. Объявления в исходном коде, metadata annotations, парсеры, объявления настроек, CMake helpers и тесты авторитетны для текущего поведения.
  2. Генерируемые канонические модели контрактов нормализуют эти источники в стабильные идентификаторы и машиночитаемый JSON.
  3. Справочные страницы для людей, локализованные индексы публичных контрактов и корневой legacy-маршрут PUBLIC_API.md генерируются из этих моделей и дополняются примерами и объяснениями, ориентированными на задачи.
  4. Ручной текст не должен поддерживать количества объявлений, сигнатуры или междоменные реестры, которые можно сгенерировать. Docs/generated/source-inventory.json остаётся независимым реестром исходников; восемнадцать моделей контрактов владеют соответствующими повторно используемыми поверхностями.
  5. Документация проекта может описывать использование API движка, но не может повышать метку стабильности этого API.

Классификация, принадлежащая исходному коду

Символы нативной кодогенерации используют отдельный metadata tag ///@ ApiContract <selector> <label> .... Селектор сначала разрешается по каноническому сгенерированному id, затем по family_id, чтобы одно проверенное объявление могло охватывать семейство перегрузок. Зарезервированный селектор scope:native-codegen классифицирует всю текущую модель и требует пины SymbolCount и InventorySha256 для отсортированного инвентаря стабильных ID. Tag разбирается и проверяется BuildTools/codegen.py, но относится только к документации и исключён из runtime compatibility hash.

Без корректного scope-контракта неаннотированные символы остаются internal (default). Корректный scope-контракт применяет проверенную метку, только пока совпадают оба пина инвентаря; любое добавление или удаление символа либо изменение стабильного ID останавливает генерацию до обновления пинов владельцем. Точное объявление символа может переопределить scope, а пересекающиеся exact/family declarations остаются недопустимыми. Явный tag internal фиксирует проверенное решение, не повышая статус символа. Для stable и experimental требуется Since; для deprecated требуются версия объявления устаревшим, существующая замена и цель удаления.

Contract tags не заменяют проверку владельцем. Текущее scope-объявление классифицирует все 2 527 сгенерированных символов как привязанные к ревизии experimental с версии 2022.1.0.wip, а вспомогательный метод разработки Game.BreakIntoDebugger сохраняет явный статус internal. Это делает интеграционную поверхность пригодной для использования и отслеживания изменений, не заявляя широкой совместимости stable.

Домены контрактов

Модель стабильности применяется отдельно к следующим областям:

  • build helpers, options, stages и BuildTools CLI;
  • настройки и ключи конфигурации;
  • методы скриптов, глобальные функции, типы, enums, события, remote calls и свойства;
  • нативные hooks и ABI расширений;
  • ABI хоста и runtime клиента и протокол updater;
  • сериализованные данные, сущности базы данных и metadata миграций;
  • сетевой протокол и версия совместимости;
  • авторские и сгенерированные форматы файлов;
  • структура пакетов и матрица поддерживаемых платформ.

Выпуск может поддерживать один домен, не обещая стабильность каждого домена.

Политика изменений

Несовместимое изменение поверхности stable требует:

  1. API diff или эквивалентное обнаружение, привязанное к исходникам;
  2. явную классификацию breaking change;
  3. инструкции по миграции и замену, когда она применима;
  4. решение для release notes и политики поддержки;
  5. обновление версии совместимости или metadata миграции, когда этого требует сетевой или сериализованный контракт;
  6. одновременное обновление сгенерированного справочника, примеров и тестов.

Скрытые fallback aliases, недокументированные compatibility shims и устаревшие дублирующие перегрузки не являются стандартной стратегией миграции. Слой совместимости добавляется только по требованию политики поддержки и получает владельца и условие удаления.

Для поверхности experimental breaking changes всё равно требуют явной записи в changelog или release notes, чтобы пользователи и ИИ-агенты могли выбрать правильную ревизию.

Автоматическая проверка генерируемых контрактов

BuildTools/docs_contract_diff.py сравнивает все восемнадцать моделируемых доменов с теми же путями на базовом SHA pull request или ревизии перед push: native API, CMake, основной BuildTools CLI, package, helper CLI, native extension, prototype, map, model, text, effect, image, particle, font, audio, video, GUI runtime и AiControl protocol. Специализированный нативный слой BuildTools/docs_api_diff.py сохраняет семантику символов и перегрузок; остальные модели сопоставляют принадлежащие исходникам стабильные идентификаторы. Все домены классифицируют добавления, документацию, политику и breaking changes относительно стабильности baseline, поэтому одновременное понижение до internal не может обойти проверку.

Для вспомогательных скриптов каждый исполняемый create_parser() остаётся источником истины синтаксиса, а BuildTools/HelperCliInterface.json владеет стабильной идентичностью helper, назначением, аудиторией, владельцем вызова и явными исключениями. Проверка AST inventory отклоняет новый открытый helper parser, который не смоделирован и не отнесён к другому каноническому домену. Домен helper CLI остаётся internal, пока владелец не утвердит версионированную политику поддержки.

Для нативного C++ проекта BuildTools/NativeExtensionInterface.json моделирует роли исходников, используемые текущими targets, поддерживаемые сигнатуры hooks, fallback, call sites и правила bindings. Структурные тесты и тесты генератора сопоставляют его с текущим поведением CMake/codegen; runtime input он не является. Домен имеет статус experimental и совместим по исходному коду только на закреплённой ревизии движка; он не обещает бинарный ABI между ревизиями. Проектные реализации и внешние SDK остаются вне контракта движка.

Breaking change объявления stable, experimental или deprecated требует точной записи схемы v2 в накопительном реестре Docs/contract-change-dispositions.json. Запись содержит домен, классификацию владельца, обоснование, миграцию, release note и обработку совместимости и привязана к change ID и baseline/current digest соответствующего домена. Изменения источника модели, области модели и контракта уровня модели также требуют решения. Изменения внутренних записей остаются видимыми в отчёте, но не становятся обещаниями совместимости.

CI записывает JSON/Markdown-отчёты для пары ревизий в Workspace/, проверяет отсутствие пропущенных решений и загружает отчёт даже при ошибке. Текущий сайт не публикует исторические снимки только ради этой проверки.

Версионирование

Документация описывает текущую ветку, пока у движка не появятся осмысленные теги выпусков и support matrix. Исторические снимки API не создаются только потому, что слой публикации умеет их размещать. После появления поддерживаемых линий выпусков сгенерированные модели и страницы закрепляются на неизменяемых тегах или ревизиях.

Последствия

Положительные

  • Разработчики могут отличать контракт от реализации и примеров.
  • Рефакторинг движка остаётся возможным, поскольку неклассифицированные внутренние поверхности не замораживаются случайно.
  • Breaking changes становятся видимыми, проверяемыми и снабжёнными миграцией.
  • Генерируемый JSON предоставляет инструментам документации и ИИ-системам стабильные идентификаторы символов.
  • Проектные примеры не могут молча переопределять гарантии движка.

Издержки

  • Существующие поверхности необходимо инвентаризировать и классифицировать до выдачи широких обещаний stable.
  • Аннотации и описания в исходном коде требуют улучшения там, где сгенерированный справочник недостаточно содержателен.
  • Междоменная автоматизация diff и её накопительный реестр решений требуют постоянного сопровождения по мере расширения доменов контрактов и политики выпусков.
  • При изменении стабильного контракта сопровождающие должны писать руководство по миграции.

Отклонённые варианты

  • Всё доступное является публичным и стабильным: отклонено, поскольку замораживает внутреннюю реализацию и противоречит текущему рефакторингу.
  • Полное отсутствие обещаний стабильности: отклонено, поскольку разработчикам игр нужна надёжная поверхность интеграции.
  • Ручные списки в PUBLIC_API.md: отклонено, поскольку сигнатуры, количества и владение расходятся с исходным кодом.
  • Использование в проекте определяет стабильность: отклонено, поскольку зависимость одного проекта является свидетельством, а не гарантией движка для всех пользователей.

Проверка

  • BuildTools/docs_inventory.py --check закрепляет независимый inventory экспортов, тестов и настроек, полученный из исходного кода.
  • BuildTools/docs_api.py --check закрепляет каноническую модель нативной кодогенерации, включая family/symbol IDs, описания, явное или default происхождение контракта, metadata жизненного цикла, сигнатуры и происхождение объявления.
  • BuildTools/docs_reference.py --check закрепляет сгенерированные страницы для людей из модели нативной кодогенерации.
  • Фокусные тесты доказывают, что селекторы семейства ApiContract охватывают перегрузки, устаревшие count и hash scope-контракта отклоняются, точные переопределения scope работают, замены deprecated разрешаются, некорректные селекторы отклоняются, а аннотации только для документации не изменяют compatibility hash.
  • BuildTools/docs_contract_diff.py --enforce, специализированный API comparator и их фокусные тесты отклоняют removal, shape change и stability withdrawal публичных baseline-записей без решения, устаревшие digest и изменения контракта модели или parser во всех восемнадцати генерируемых доменах.
  • BuildTools/docs_public_api.py --check пересобирает корневой индекс контрактов из этих восемнадцати моделей и отклоняет устаревшие ссылки доменов, метки или количества нативных символов.
  • Широкая классификация stable остаётся задачей владельцев и политики выпусков; закреплённый инвентарём scope со статусом experimental сам по себе не обещает стабильную совместимость между ревизиями.
  • Code review должен отклонять текст, который представляет неклассифицированный доступный символ как stable.

Связанные документы

Введите запрос.