Files
dzentra_bot/docs/migrations/build_060_22.md

38 KiB
Raw Permalink Blame History

Build 060.22 — Acquisition Runtime Service

Engineering Migration Report


Контроль документа

Свойство Значение
Build 060.22
Название Acquisition Runtime Service
Статус Completed
Проект Dzentra
Подсистема Market Data Acquisition
Компонент Acquisition Runtime Service Layer
Версия 1.0

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


Цель Build

Build 060.21 завершил формирование контрактного слоя Acquisition Runtime.

После предыдущего этапа в системе уже существовали:

AcquisitionRuntimeCommand
AcquisitionRuntimeEvent
AcquisitionRuntimeCommandDispatcherProtocol
AcquisitionRuntimeEventPublisherProtocol

Также были доступны базовые WebSocket-контракты:

WebSocketTransportProtocol
WebSocketSessionProtocol
WebSocketSubscriptionManagerProtocol

Однако Runtime Layer оставался декларативным.

Команды описывали инфраструктурные намерения, но в системе отсутствовал производственный компонент, который:

  • принимал типизированную Runtime-команду;
  • определял её конкретный тип;
  • выбирал соответствующую инфраструктурную зависимость;
  • выполнял одну детерминированную операцию;
  • сохранял независимость от Trades Feed, Consistency и Recovery.

Главной задачей Build 060.22 стало создание первого исполняемого сервисного компонента Acquisition Runtime:

AcquisitionRuntimeService

Одновременно был введён отдельный публичный контракт сервисного уровня:

AcquisitionRuntimeServiceProtocol

После завершения Build система получает:

  • единый сервис исполнения Acquisition Runtime Commands;
  • детерминированную маршрутизацию всех шести существующих команд;
  • отдельный публичный Protocol сервиса;
  • расширенный контракт управления WebSocket-подписками;
  • полное unit-покрытие нового сервисного слоя;
  • подтверждённую регрессионную совместимость Runtime, Consistency, Recovery и Feeds.

При этом Build сознательно не выполняет интеграцию Runtime Service в корневой Acquisition Service или Trades Feed.

Эта задача относится к Build 060.23.


Предпосылки

К началу настоящего Build Runtime Layer уже обладал полным набором неизменяемых команд:

ConnectCommand
DisconnectCommand
SubscribeCommand
UnsubscribeCommand
SendTextCommand
SendBinaryCommand

Также существовали инфраструктурные события:

ConnectedEvent
DisconnectedEvent
ConnectFailedEvent
MessageReceivedEvent
MessageSentEvent
ReconnectStartedEvent
ReconnectCompletedEvent
ReconnectFailedEvent
HeartbeatTimeoutEvent

Build 060.21 определил публичные границы передачи команд и событий, но сознательно не создавал исполняемую реализацию.

Архитектура выглядела следующим образом:

AcquisitionRuntimeCommand
            │
            ▼
AcquisitionRuntimeCommandDispatcherProtocol
            │
            ▼
      (реализация отсутствует)

Таким образом следующий этап должен был закрыть именно сервисную границу между типизированными командами и существующими инфраструктурными компонентами.


Результаты архитектурного аудита

Перед началом реализации был выполнен аудит следующих компонентов:

src/market_data/acquisition/runtime/websocket_protocol.py
src/market_data/acquisition/runtime/runtime_commands.py
src/market_data/acquisition/runtime/runtime_events.py
src/market_data/acquisition/runtime/transport_messages.py
src/market_data/acquisition/runtime/supervisor.py
src/market_data/acquisition/runtime/reconnect.py
src/market_data/acquisition/runtime/scheduler.py
src/market_data/acquisition/runtime/heartbeat.py

Дополнительно были проанализированы:

src/market_data/acquisition/service.py
src/market_data/acquisition/protocol.py
src/market_data/acquisition/registry.py

а также соответствующие unit-тесты.

Аудит подтвердил следующее.

Runtime orchestration ещё не реализован

Файлы:

supervisor.py
reconnect.py
scheduler.py
heartbeat.py

не содержали производственной реализации.

Следовательно Build 060.22 не являлся рефакторингом существующего Runtime Service.

Он создавал первую минимальную сервисную реализацию с нуля.


Supervisor преждевременен

На раннем этапе рассматривалась возможность реализовать сервис внутри:

supervisor.py

Данный вариант был отклонён.

Supervisor является компонентом более высокого уровня и должен координировать:

  • Runtime Service;
  • reconnect;
  • heartbeat;
  • scheduler;
  • lifecycle длительно работающей подсистемы.

Поскольку перечисленные механизмы ещё не реализованы, использование Supervisor в Build 060.22 создало бы преждевременную orchestration-логику.

Поэтому был введён отдельный тонкий сервис:

AcquisitionRuntimeService

Registry для Runtime Service не требуется

Существующие Feed Registry предназначены для выбора одной реализации из нескольких зарегистрированных источников.

Runtime Service в текущей архитектуре существует в единственном экземпляре и не представляет семейство взаимозаменяемых Feed-реализаций.

Создание отдельного Runtime Registry было признано искусственным и исключено из Scope Build.


Корневые Acquisition-файлы не изменяются

Аудит подтвердил отсутствие необходимости изменять:

src/market_data/acquisition/service.py
src/market_data/acquisition/protocol.py
src/market_data/acquisition/registry.py

Интеграция нового Runtime Service с Acquisition Layer относится к следующему этапу:

Build 060.23 — Acquisition Integration

Архитектурное решение

По результатам аудита была утверждена следующая модель.

AcquisitionRuntimeCommand
            │
            ▼
AcquisitionRuntimeServiceProtocol
            │
            ▼
AcquisitionRuntimeService
            │
      ┌─────┼──────────────┐
      ▼     ▼              ▼
 Session  Transport  Subscription Manager

AcquisitionRuntimeService реализует одновременно:

AcquisitionRuntimeServiceProtocol
AcquisitionRuntimeCommandDispatcherProtocol

Сервис не реализует WebSocket самостоятельно.

Он только делегирует каждую команду одной заранее определённой инфраструктурной зависимости.


Правило уникальности имён файлов

Build 060.22 продолжает обязательное архитектурное правило проекта Dzentra:

Во всём репозитории не допускается создание нескольких новых файлов с одинаковым именем независимо от их расположения в каталогах.

Единственным служебным исключением остаётся:

__init__.py

Поэтому Build сознательно не создаёт файлы с общими именами:

service.py
protocol.py
test_service.py

Вместо них добавлены уникальные имена:

acquisition_runtime_service.py
acquisition_runtime_service_protocol.py
test_acquisition_runtime_service.py

Имена однозначно отражают назначение файлов без необходимости учитывать родительский каталог.

Repository-wide поиск показал, что в проекте уже существуют старые файлы с общими именами, созданные до введения данного правила.

Build 060.22 не добавляет новых файлов с такими именами и не выполняет ретроспективное переименование существующего рабочего кода вне согласованного Scope.


AcquisitionRuntimeServiceProtocol

В рамках Build создан новый публичный контракт:

AcquisitionRuntimeServiceProtocol

Файл:

src/market_data/acquisition/runtime/
acquisition_runtime_service_protocol.py

Protocol определяет единственную публичную операцию:

async def dispatch(
    command: AcquisitionRuntimeCommand,
) -> None

Другие публичные методы не вводятся.

В частности отсутствуют:

connect()
disconnect()
subscribe()
unsubscribe()
send()

Все операции Runtime выражаются исключительно через типизированные команды.

Это исключает появление второго параллельного API для одной и той же инфраструктурной операции.


Почему Service Protocol существует отдельно

В Build 060.21 уже был добавлен:

AcquisitionRuntimeCommandDispatcherProtocol

Он определяет инфраструктурную способность принимать команды.

Build 060.22 дополнительно вводит:

AcquisitionRuntimeServiceProtocol

Данный контракт фиксирует отдельную сервисную границу Runtime Layer.

Такое разделение позволяет последующим компонентам зависеть именно от сервисной абстракции, а не от конкретной реализации.

В будущем под тем же Protocol могут существовать различные реализации:

Simple Acquisition Runtime Service
Queued Acquisition Runtime Service
Supervised Acquisition Runtime Service

Build 060.22 реализует только минимальный прямой вариант.


AcquisitionRuntimeService

Главным production-компонентом Build становится:

AcquisitionRuntimeService

Файл:

src/market_data/acquisition/runtime/
acquisition_runtime_service.py

Сервис является тонким stateless-координатором.

Он:

  • принимает одну типизированную команду;
  • определяет её конкретный тип;
  • вызывает соответствующую инфраструктурную зависимость;
  • завершает выполнение после завершения делегированной операции.

Сервис не:

  • создаёт фоновые задачи;
  • хранит очередь команд;
  • выполняет retries;
  • управляет reconnect;
  • запускает heartbeat;
  • хранит подписки;
  • обрабатывает Trade;
  • вызывает Consistency;
  • вызывает Recovery.

Зависимости сервиса

Все зависимости передаются через конструктор.

AcquisitionRuntimeService
        │
        ├── WebSocketSessionProtocol
        ├── WebSocketTransportProtocol
        ├── WebSocketSubscriptionManagerProtocol
        └── AcquisitionRuntimeEventPublisherProtocol

Сервис не создаёт зависимости самостоятельно.

Создание графа объектов относится к Composition Root и не входит в Scope Build 060.22.


Почему Event Publisher внедрён, но пока не используется

Конструктор Runtime Service принимает:

AcquisitionRuntimeEventPublisherProtocol

Однако Build 060.22 не публикует события после выполнения команд.

Это осознанное решение.

Publisher включён в окончательную форму конструктора, чтобы последующие Build могли подключить публикацию событий без изменения публичной композиции сервиса.

При этом преждевременная event-orchestration не добавляется.

Unit-тест отдельно подтверждает, что Publisher пока остаётся неиспользованным.


Расширение WebSocketSubscriptionManagerProtocol

Во время реализации было обнаружено расхождение между первоначальной архитектурной матрицей и фактическим контрактом Subscription Manager.

До Build Protocol содержал только:

restore_subscriptions()
clear_subscriptions()

Но Runtime Service должен исполнять:

SubscribeCommand
UnsubscribeCommand

Следовательно существующий контракт был недостаточен для детерминированной маршрутизации.

В Build WebSocketSubscriptionManagerProtocol локально расширен методами:

async def subscribe(
    subscription_key: str,
    message: AcquisitionSubscriptionMessage,
) -> None

и:

async def unsubscribe(
    subscription_key: str,
    message: AcquisitionSubscriptionMessage,
) -> None

Существующие методы:

restore_subscriptions()
clear_subscriptions()

полностью сохранены.

Расширение является обратно совместимым с точки зрения существующих моделей данных и не изменяет поведение уже реализованных компонентов.


AcquisitionSubscriptionMessage

Для операций подписки введён типовой alias:

AcquisitionSubscriptionMessage

Он объединяет:

TransportTextMessage
TransportBinaryMessage

Благодаря этому Subscription Manager принимает не произвольные str | bytes, а уже существующие неизменяемые модели транспортных сообщений.

Это обеспечивает соответствие моделям:

SubscribeCommand
UnsubscribeCommand

и исключает повторную интерпретацию payload внутри Runtime Service.


Официальная матрица маршрутизации

Build 060.22 реализует следующую детерминированную таблицу.

Runtime Command Действие
ConnectCommand WebSocketSessionProtocol.start()
DisconnectCommand WebSocketSessionProtocol.stop()
SubscribeCommand WebSocketSubscriptionManagerProtocol.subscribe(...)
UnsubscribeCommand WebSocketSubscriptionManagerProtocol.unsubscribe(...)
SendTextCommand WebSocketTransportProtocol.send(message.payload)
SendBinaryCommand WebSocketTransportProtocol.send(message.payload)

Каждый тип команды имеет ровно один маршрут.

Сервис не выполняет дополнительные действия и не вызывает несколько зависимостей для одной команды.


ConnectCommand

Маршрут:

ConnectCommand
        │
        ▼
WebSocketSessionProtocol.start()

Runtime Service не вызывает transport.connect() напрямую.

Жизненный цикл соединения принадлежит Session.


DisconnectCommand

Маршрут:

DisconnectCommand
        │
        ▼
WebSocketSessionProtocol.stop()

Runtime Service не вызывает transport.disconnect() напрямую.

Завершение сессии остаётся обязанностью Session.


SubscribeCommand

Маршрут:

SubscribeCommand
        │
        ▼
WebSocketSubscriptionManagerProtocol.subscribe(
    subscription_key,
    message,
)

Runtime Service передаёт исходные значения без изменения.

Сервис не хранит список подписок и не сериализует сообщение.


UnsubscribeCommand

Маршрут:

UnsubscribeCommand
        │
        ▼
WebSocketSubscriptionManagerProtocol.unsubscribe(
    subscription_key,
    message,
)

Сервис не изменяет внутреннее состояние Subscription Manager самостоятельно.


SendTextCommand

Маршрут:

SendTextCommand
        │
        ▼
WebSocketTransportProtocol.send(str)

В Transport передаётся:

command.message.payload

Runtime Service не вводит отдельный метод send_text() и не расширяет Transport Protocol без необходимости.


SendBinaryCommand

Маршрут:

SendBinaryCommand
        │
        ▼
WebSocketTransportProtocol.send(bytes)

В Transport передаётся бинарный payload без преобразования.

Существующий единый метод:

send(message: str | bytes)

полностью покрывает оба типа сообщений.


Обработка неизвестной команды

Тип AcquisitionRuntimeCommand ограничивает штатный публичный вход сервиса.

Однако Runtime Service также содержит защитную ветку для произвольного неподдерживаемого объекта.

В этом случае генерируется:

TypeError

с диагностическим сообщением, включающим имя фактического типа.

Это гарантирует, что появление новой Runtime-команды потребует явного обновления маршрутизации и не останется незамеченным.


Обработка исключений

Build не вводит собственную иерархию ошибок Runtime Service.

Любое исключение инфраструктурной зависимости распространяется вызывающему компоненту без обёртки.

Например:

ConnectCommand
        │
        ▼
AcquisitionRuntimeService
        │
        ▼
WebSocketSession.start()
        │
        ▼
RuntimeError

Сервис не преобразует ошибку в общий RuntimeDispatchError.

Такое решение сохраняет:

  • исходный тип исключения;
  • исходное диагностическое сообщение;
  • прозрачность инфраструктурного поведения;
  • возможность точной обработки на более высоком уровне.

Stateless-архитектура сервиса

AcquisitionRuntimeService не хранит собственное операционное состояние.

Внутри объекта сохраняются только внедрённые зависимости.

Сервис не хранит:

  • состояние соединения;
  • выполненные команды;
  • активные подписки;
  • историю отправленных сообщений;
  • reconnect attempts;
  • Recovery state;
  • Consistency state.

Все перечисленные данные принадлежат специализированным компонентам.


Изменённые файлы

Новый Service Protocol

src/market_data/acquisition/runtime/
acquisition_runtime_service_protocol.py

Добавлен:

AcquisitionRuntimeServiceProtocol

Protocol содержит единственный асинхронный метод dispatch().


Новый Runtime Service

src/market_data/acquisition/runtime/
acquisition_runtime_service.py

Добавлен:

AcquisitionRuntimeService

Класс реализует:

AcquisitionRuntimeServiceProtocol
AcquisitionRuntimeCommandDispatcherProtocol

и маршрутизирует все шесть Runtime Commands.


Расширение WebSocket Protocol Layer

src/market_data/acquisition/runtime/websocket_protocol.py

Добавлены:

AcquisitionSubscriptionMessage

и методы:

WebSocketSubscriptionManagerProtocol.subscribe()
WebSocketSubscriptionManagerProtocol.unsubscribe()

Существующие Protocol и alias сохранены.


Unit-тест Runtime Service

tests/unit/market_data/acquisition/runtime/
test_acquisition_runtime_service.py

Добавлены тестовые реализации:

  • FakeSession;
  • FakeTransport;
  • FakeSubscriptionManager;
  • FakeEventPublisher.

Проверена маршрутизация всех поддерживаемых команд и распространение исключений.


Обновление теста WebSocket Protocol

tests/unit/market_data/acquisition/runtime/
test_websocket_protocol.py

FakeWebSocketSubscriptionManager расширен методами:

subscribe()
unsubscribe()

Также добавлена негативная проверка неполной реализации обновлённого Protocol.


Файлы, которые не изменялись

Build не изменяет:

src/market_data/acquisition/runtime/runtime_commands.py
src/market_data/acquisition/runtime/runtime_events.py
src/market_data/acquisition/runtime/transport_messages.py
src/market_data/acquisition/runtime/supervisor.py
src/market_data/acquisition/runtime/reconnect.py
src/market_data/acquisition/runtime/scheduler.py
src/market_data/acquisition/runtime/heartbeat.py

Также не изменяются:

  • корневой Acquisition Service;
  • Acquisition Registry;
  • Acquisition Protocol;
  • Trades Feed;
  • Quotes Feed;
  • Candles Feed;
  • Trade Stream Consistency;
  • Trade Stream State Store;
  • Trade Recovery;
  • WebSocket adapters;
  • каноническая модель Trade.

Unit-тестирование AcquisitionRuntimeService

Новый сервис покрыт отдельным набором unit-тестов.

Проверены следующие сценарии:

  • сервис соответствует AcquisitionRuntimeServiceProtocol;
  • сервис соответствует AcquisitionRuntimeCommandDispatcherProtocol;
  • ConnectCommand вызывает только session.start();
  • DisconnectCommand вызывает только session.stop();
  • SubscribeCommand передаёт исходные subscription_key и message;
  • UnsubscribeCommand передаёт исходные subscription_key и message;
  • SendTextCommand передаёт строковый payload в transport.send();
  • SendBinaryCommand передаёт бинарный payload в transport.send();
  • Event Publisher не используется преждевременно;
  • неизвестная команда приводит к TypeError;
  • исключение Session распространяется без изменения.

Результат:

11 passed

Unit-тестирование WebSocket Protocol

После расширения Subscription Manager выполнен локальный прогон обновлённого Protocol Layer.

Проверены:

  • полная реализация Transport;
  • полная реализация Session;
  • полная реализация Subscription Manager;
  • Command Dispatcher;
  • Event Publisher;
  • отрицательные сценарии неполных реализаций.

Результат:

9 passed

Регрессионное тестирование Runtime Layer

После добавления Runtime Service выполнен полный прогон:

tests/unit/market_data/acquisition/runtime

Проверены:

  • Acquisition Runtime Service;
  • Runtime Commands;
  • Runtime Events;
  • Transport Messages;
  • WebSocket Protocol Layer.

Результат:

46 passed

Это подтверждает, что новый сервисный слой не нарушил существующее поведение Runtime.


Расширенное регрессионное тестирование

Для подтверждения архитектурной изоляции выполнен совместный прогон:

Runtime
Consistency
Recovery
Feeds

Команда охватывала каталоги:

tests/unit/market_data/acquisition/runtime
tests/unit/market_data/acquisition/consistency
tests/unit/market_data/acquisition/recovery
tests/unit/market_data/acquisition/feeds

Результат:

180 passed

Тем самым подтверждено:

  • Runtime Service работает корректно;
  • обновление Subscription Manager не нарушило Protocol Layer;
  • Trade Stream Consistency не затронута;
  • Trade Stream State Store не затронут;
  • Trade Recovery не затронута;
  • существующие Feed продолжают работать без изменений;
  • преждевременная интеграция Build 060.23 не произошла.

Обратная совместимость

Build сохраняет функциональное поведение существующих подсистем.

Не изменились:

  • Runtime Command models;
  • Runtime Event models;
  • Transport Message models;
  • WebSocket Transport API;
  • WebSocket Session API;
  • Canonical Trade Model;
  • Trades Feed API;
  • Consistency API;
  • Recovery API;
  • Feed Registry;
  • корневые Acquisition Services.

Единственное расширение существующего контракта относится к WebSocketSubscriptionManagerProtocol и добавляет операции, уже необходимые существующим SubscribeCommand и UnsubscribeCommand.


Производительность

Runtime Service выполняет конечную цепочку isinstance-проверок по шести типам команд и один делегированный асинхронный вызов.

Сервис не вводит:

  • очереди;
  • дополнительные копии payload;
  • сериализацию;
  • фоновые задачи;
  • блокировки;
  • retries;
  • хранение истории.

Следовательно накладные расходы ограничены постоянным временем:

O(1)

для каждой команды.

Основная задержка полностью определяется вызываемой инфраструктурной зависимостью.


Подтверждённые архитектурные инварианты

Runtime Service не знает предметную область

Сервис не импортирует:

Trade
TradesFeed
TradeStreamConsistencyProtocol
TradeRecoveryProtocol
TradeStreamStateStore

Runtime Service stateless

Сервис хранит только ссылки на внедрённые зависимости.


Одна команда имеет один маршрут

Ни одна команда не выполняет несколько инфраструктурных операций.


Session владеет lifecycle соединения

ConnectCommand и DisconnectCommand делегируются Session, а не Transport напрямую.


Subscription Manager владеет подписками

Runtime Service не хранит список подписок и не управляет их состоянием самостоятельно.


Transport владеет отправкой payload

Text и Binary payload передаются через существующий единый метод send().


Event publication не реализована преждевременно

Publisher внедрён, но не используется до отдельного этапа интеграции событий.


Consistency и Recovery не зависят от Runtime Service

Новые импорты и зависимости в данных подсистемах отсутствуют.


Уникальность новых имён файлов соблюдена

Build не добавляет новых service.py, protocol.py или test_service.py.


Что не входит в Scope Build

Настоящий Build сознательно не реализует:

  • интеграцию Runtime Service в корневой Acquisition Service;
  • интеграцию Runtime Service с Trades Feed;
  • Composition Root;
  • Runtime Supervisor;
  • Reconnect orchestration;
  • Heartbeat;
  • Scheduler;
  • очередь команд;
  • background worker;
  • публикацию Runtime Events;
  • обработку входящих WebSocket-сообщений;
  • маршрутизацию сообщений к Trade Adapter;
  • запуск Trade Stream Consistency;
  • автоматический Trade Recovery;
  • восстановление подписок после reconnect;
  • реальный WebSocket transport implementation;
  • сохранение Runtime-состояния между перезапусками.

Отсутствие перечисленных компонентов является осознанной границей Build и не рассматривается как незавершённость.


Архитектурное значение Build

Build 060.22 является первым этапом серии Runtime, добавляющим исполняемое поведение.

До него Runtime Layer содержал:

  • модели сообщений;
  • команды;
  • события;
  • Protocol.

После завершения Build появляется единая точка выполнения команд:

AcquisitionRuntimeService

Это превращает Runtime из исключительно декларативного слоя в минимальную работающую сервисную подсистему.

При этом сервис остаётся полностью изолированным от предметной области и готов к последующей интеграции без изменения уже реализованных Consistency, Recovery и Feed.


Связь с последующими Build

Ретроспективное уточнение 060.30.5 (2026-08-03).

Названия ниже сохраняют план на момент завершения 060.22. Фактически 060.24 завершил внутреннюю Runtime Recovery, 060.25 — Production Runtime Integration, 060.26 — Integration & Regression, 060.27060.29 — Storage/Checkpoint/Access, а Final Documentation выполняется в 060.30. Актуальная последовательность: Master Roadmap. После 060.30 утверждён только 061.00; номера следующих Feed-веток ещё не назначены.

Build 060.23 — Acquisition Integration

Новый Runtime Service будет подключён к верхнему уровню Acquisition и станет доступен через утверждённую композицию зависимостей.


Build 060.24 — Reconnect & Runtime Recovery

На основе Runtime Service будут построены:

  • reconnect orchestration;
  • resubscribe;
  • восстановление Runtime-потока;
  • вызов Trade Recovery после нарушения непрерывности.

Build 060.25 — Integration & Regression

Будет выполнена полная проверка взаимодействия Runtime, Acquisition, Trades Feed, Consistency и Recovery.


Заключение

Build 060.22 завершает создание минимального исполняемого сервисного слоя Acquisition Runtime.

В рамках этапа были реализованы:

AcquisitionRuntimeServiceProtocol
AcquisitionRuntimeService

а также расширен:

WebSocketSubscriptionManagerProtocol

для поддержки существующих команд подписки и отмены подписки.

Runtime Service детерминированно маршрутизирует все шесть команд к соответствующим инфраструктурным зависимостям и не содержит знаний о рыночных данных.

Архитектура сохраняет строгие границы:

  • Session управляет lifecycle;
  • Transport отправляет payload;
  • Subscription Manager управляет подписками;
  • Runtime Service только координирует;
  • Consistency и Recovery остаются независимыми.

Все изменения подтверждены локальными и расширенными regression-тестами.


Итог Build

После завершения Build 060.22 система обладает следующими возможностями.

✓ Создан отдельный публичный AcquisitionRuntimeServiceProtocol.

✓ Реализован AcquisitionRuntimeService.

✓ Поддерживаются все шесть существующих Acquisition Runtime Commands.

✓ Каждая команда имеет один детерминированный маршрут.

WebSocketSubscriptionManagerProtocol поддерживает subscribe и unsubscribe.

✓ Text и Binary payload отправляются через существующий Transport API.

✓ Исключения инфраструктурных зависимостей распространяются без изменения.

✓ Event Publisher не используется преждевременно.

Все новые файлы имеют уникальные имена.

✓ Runtime regression завершён результатом 46 passed.

✓ Расширенный regression завершён результатом 180 passed.

Build 060.22 — Acquisition Runtime Service считается полностью завершённым и готовым к фиксации в Git.