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

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

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

Контекст

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

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

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

Решение

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

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

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

Пока символ или поверхность не классифицированы явно, они считаются 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 native-codegen как привязанный к ревизии experimental, с точными внутренними переопределениями. Исторические аннотации Since сохраняют первоначальные значения; никакая метка не отменяет сохранение смысла.

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

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

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

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

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

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

Существующие допустимые значения сохраняют первоначальный смысл: имена и числовые ID, единицы/знаки, специальные значения/значения по умолчанию, поведение входов/выходов, владение, порядок и побочные эффекты. Добавление и расширение сохраняют прежние случаи. Нельзя переиспользовать удалённое значение для переинтерпретации исходников, контента и сохранённых данных. Исправление документированного дефекта восстанавливает установленный смысл и требует регрессии этого инварианта; намеренная смена допустимого старого случая является заменой.

Замена должна получить отдельный идентификатор/тип/ключ или явную границу формата, удалить прежнюю поверхность и завершать затронутое старое использование ошибкой компиляции, перепекания или валидации. Если вызов прежней формы нельзя диагностировать, измените границу и добавьте валидатор во владении исходников. Проект без миграции либо сохраняет корректное поведение используемых контрактов, либо получает однозначные ошибки до обычного игрового процесса. Неожиданности выполнения, разрешающий разбор, автоматические значения по умолчанию, запасные алиасы и шимы устаревшего API запрещены.

Существующее преобразование сохранённых свойств/ссылок сущностей через MigrationRule — узкое исключение для миграции данных. Оно не сохраняет старый API вызовов/конфигурации; миграции бизнес-данных проекта остаются у проекта. Хеши совместимости, ABI и схемы требуют отдельных явных решений. Истекающий маркер отказа старому бинарнику не разрешает переходный старый API.

Каждое удаление или замена сопровождается исчерпывающими двуязычными записями миграции: точные версии/поверхности, обнаружение и диагностика, полные преобразования и примеры, упорядоченные сборка/перепекание, сохранённые данные, совместимость/развёртывание/откат и свидетельства приёмки. Нужны отрицательные проверки затронутого старого использования и положительные проверки незатронутого/мигрированного. Пустой генерируемый diff не доказывает сохранение смысла. Каждый шаг master также выполняет обязательный список обновления версии/чейнджлогов/документации из этого руководства.

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

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, 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/, проверяет отсутствие пропущенных решений и загружает отчёт даже при ошибке. Текущий сайт не публикует исторические снимки только ради этой проверки.

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

Каждое обновление master и релизной ветки следует правилам версий Engine, включая счётчики веток, датированные миграции EN/RU, генерируемую документацию и проверки. Будущие патчи сохраняют фиксированный срез; поддержка не возвращает старые API в master.

Документация описывает текущую ветку, пока у движка не появятся осмысленные теги выпусков и 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.

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

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