exchanger/ROADMAP.md

218 KiB
Raw Permalink Blame History

Дорожная карта проекта Exchanger

1. Назначение документа

Этот файл — единая точка отслеживания разработки полноэкранного приложения вендингового аппарата для выдачи жетонов. Он фиксирует текущее состояние, принятые архитектурные решения, порядок реализации, критерии готовности, риски и вопросы, требующие согласования.

Дата формирования первоначального плана: 2026-08-20.

Последняя актуализация состояния: 2026-09-07.

2. Легенда статусов

  • [ ] — задача не начата.
  • [~] — задача выполняется.
  • [x] — задача выполнена и проверена.
  • [!] — задача заблокирована внешней зависимостью или решением.
  • [?] — требуется уточнение или согласование.

Правило обновления: задача получает [x] только после выполнения указанных для неё проверок и критериев приёмки. При завершении этапа следует добавлять короткую запись в журнал прогресса в конце файла.

3. Цель продукта

Создать на Godot 4.7 Mono/C# надёжное портретное киоск-приложение для напольного разменника жетонов. Приложение должно:

  1. Показывать интерфейс с единственной встроенной fallback-темой, заменяемой при запуске одним внешним DLC со статическим фоном и отдельными анимированными 2D-спрайтами.
  2. Показывать цену жетона, бинарное состояние запаса «много/мало» и бонусные условия.
  3. Поддерживать отдельные сценарии оплаты наличными и картой.
  4. Безопасно отправлять команду выдачи жетонов через последовательный порт.
  5. Дожидаться результата выдачи и показывать успех либо понятную ошибку.
  6. Возвращаться в исходное состояние по таймаутам и после завершения сессии.
  7. Работать прежде всего на Windows x64 и не связывать UI с конкретной реализацией порта, чтобы в будущем заменить её на Android USB-Serial.
  8. Воспроизводить промо-контент на домашнем экране без влияния на основной сценарий.
  9. Соответствовать портретному корпусу и пиксельной космической айдентике из подтверждённых референсов.

4. Текущее состояние проекта

  • Создан проект Exchanger на Godot 4.7.1 Mono.
  • Настроена сборка C# под .NET 8; Android-конфигурация проекта указывает .NET 9.
  • Выбран рендерер mobile (Vulkan): он нужен runtime-расширению Native Video для MP4/MOV/M4V.
  • C#-решение собирается без ошибок и предупреждений.
  • Подключены плагины godot_ai, godotx_toast, log и reload_current_scene.
  • Подключён runtime GDExtension native_video: настройки промо принимают OGV/MP4/MOV/M4V, а export сохраняет его Windows/Linux библиотеки; внешние MP4/MOV/M4V сохраняют исходный путь. Unicode-пути Windows передаются Native Video напрямую без копий, ссылок и ASCII-псевдонимов.
  • Устранён временный кэш player/stream: Stop(), Stream = null, смена источника и QueueFree() проходят штатно; 50 циклов проверены в реальном AdvertisementPlayer.
  • Создана атомарная структура src/core, src/scenes, src/ui, src/hardware и каталогов ассетов; разработка тем полностью вынесена из репозитория приложения.
  • Созданы первые собственные C#-скрипты, ресурсы и сцены.
  • Главной сценой назначена src/scenes/App/App.tscn.
  • Добавлен пользовательский Input Action kiosk_back; устаревший debug_cycle_theme удалён вместе с переключением тем.
  • Корневой README.md полностью переписан под Exchanger.
  • Ошибочный корневой LICENSE от GodotX Toast удалён; лицензии сторонних компонентов сохранены рядом с ними.
  • Зафиксировано, что собственная лицензия Exchanger пока не объявлена.
  • Проанализированы восемь изображений и видео интерфейса; выводы сохранены в docs/REFERENCE_ANALYSIS.md.
  • Каталог references/ исключён из Git через .gitignore и отмечен как непроизводственный источник концептов.
  • Динамический shader/particle-фон удалён; активная сцена использует StaticBackground со статическим цветом/изображением и отдельным слоем анимированных спрайтов.
  • Ландшафтный StartScreen удалён и заменён портретным HomeScreen; логический viewport синхронизирован с theme-preview на 720×1280.
  • Инициализирован локальный Git-репозиторий; remote и первый коммит будут настроены отдельным решением владельца проекта.
  • Полный runtime переведён на C#/.NET 8, девять атомарных экранов и один внешний self-contained DLC; space-pixel принят по manifest v2 с 481 обязательным биндингом.
  • Подтверждён целевой DualHopperAuto: первый хоппер использует 30X, IN1 и IN24, второй — 40X, IN4 и IN25, сервисная кнопка — IN8.
  • Windows x64 release собран как один Exchanger.exe со встроенными PCK и .NET outputs; packaged smoke прошёл на mock и подтвердил авторизацию реального контроллера на COM3.
  • Windows export разделён на явные пресеты Windows x64 Release и Windows x64 Debug; staging-скрипт жёстко выбирает соответствующий --export-release или --export-debug.
  • Этапы 4 и 5 закрыты: COM-транспорт и полный MVP-сценарий реализованы и покрыты автоматическими проверками; оставшаяся физическая приёмка вынесена в раздел 4.1.
  • В разделе «Сервис» добавлено немедленное скрытие элементов Home: учётного остатка, theme-owned персонажа справа и целого промо-блока; скрытые панели исключаются из маршрутов питомца.
  • Точный учёт жетонов и запрет покупки сверх остатка управляются раздельно: можно продолжать показывать/списывать остаток, не ограничивая им оплату; отключение учёта скрывает сервисные действия.
  • [~] Добавлен и проверен one-file Linux x86_64 export для Debian 13; платформенная валидация serial-path завершена, udev/dialout и kiosk остаются открытыми.

4.1. Проверки на автомате

Этот раздел — единая очередь проверок, требующих физического автомата, его панели, контроллера, платёжной периферии или целевого системного блока. Они не переоткрывают завершённые программные этапы. Перед каждым аппаратным запуском требуется проверить фактическое подключение и занятость порта, подготовить автомат и получить отдельное разрешение на конкретную группу тестов.

Интерфейс, панель и производительность

  • Проверить читаемость с рабочего расстояния и при ярком освещении.
  • Проверить управление пальцем и в перчатках: все девять экранов, прокрутку Settings, текстовую и цифровую клавиатуры, первое касание и отсутствие действий по hover.
  • Проверить масштабирование сетки 720×1280, ориентацию, калибровку touch и размеры зон на фактическом разрешении панели.
  • Повторить performance baseline на целевом ПК: FPS, CPU-время, память и draw calls в fallback и space-pixel, в полном и облегчённом режимах.
  • Профилировать одновременную работу анимированных спрайтов DLC и декодера реального MP4/OGV-промо-ролика в Mobile/Vulkan.
  • Проверить отключение/возврат дисплея, потерю фокуса, kiosk/autostart и watchdog.

Контроллер, платежи и хопперы

  • Повторить безопасную приёмку COM3: framing, AES-handshake, точный READY!, INPUT, status-запросы, безопасное исходное состояние и несколько штатных reconnect-циклов.
  • Проверить физическое отключение/возврат USB-COM: UI не зависает, worker не дублируется, незавершённая выдача не повторяется, позднее подтверждение не завершает новую операцию.
  • Проверить IN8/SERVICE, уровни запаса IN1 и IN4, удержание кнопки без повторов и открытие/закрытие Settings с включённым и выключенным PIN.
  • Проверить монетник и купюроприёмник: включение/отключение каналов, MONEY:*, ASK:*, накопление суммы, отмену, таймаут и отсутствие двойного учёта.
  • Проверить безналичную оплату: точное совпадение суммы 163 и MONEY:CASHLESS, ASK:CASHLESS, отмену, timeout/decline и keep-alive 161 реже 180 секунд.
  • Проверить обычную выдачу обоих хопперов: 309N/409N, пакеты 1–99, APPEND=N, по одному подтверждению на каждый HOPPER1/2 OK, маршрутизацию и раздельное списание остатка.
  • Проверить частичную/ошибочную выдачу: ERROR → CREDIT=N, запросы 3090/4090, отсутствие автоматического retry и единственный итог операции.
  • Проверить сервисный пересчёт обоих хопперов: 301/300 и 401/400, учёт только HOPPER1/2 PULSE=1, завершение после 10 секунд тишины и безопасную остановку мотора.
  • Проверить три сочетания политики на реальном автомате: учёт с лимитом, учёт без лимита и полностью выключенный учёт; подтвердить блокировку card/cash до аппаратной команды, работу бонусов, продолжение списания без лимита, скрытие сервисных действий и пересчёт перед включением.
  • Проверить перезапуск/потерю питания во время операции: запись получает UnknownAfterRestart, оплата и выдача автоматически не повторяются.

Длительная и установочная приёмка

  • Выполнить непрерывный 24-часовой soak с повторными сессиями, оплатой, выдачей, SERVICE, автосохранением, ротацией логов и периодическим отключением/возвратом дисплея.
  • Провести чистую установку Windows-релиза на целевом устройстве; проверить доверие подписи, конфигурацию, внешний DLC, автозапуск, обновление и откат.
  • После программной Linux-адаптации запустить ELF на Debian 13 и принять графику, touch, внешний DLC, USB-UART, udev/dialout, kiosk/autostart и watchdog. Предварительное обнаружение выполнено: Debian распознал Silicon Labs CP2102 10c4:ea60 штатным cp210x и создал /dev/ttyUSB0; права и обмен с Exchanger ещё не проверялись.
  • Если будет выбран Android-порт, провести на целевом ARM64-устройстве export/runtime, USB permission/reconnect, Mobile renderer, touch, системные жесты, поворот, энергосбережение и полный регрессионный сценарий.

5. Зафиксированные архитектурные решения

5.1. Рендерер

Использовать Mobile renderer (Vulkan) как базовый режим. Это обязательное требование runtime-расширения Native Video для MP4/MOV/M4V; оно совместимо с 2D-пиксельным UI и целевой Windows/Linux конфигурацией. Compatibility нельзя включать, пока требуется Native Video. Для слабого GPU остаётся ReducedEffects; производительность и аппаратное декодирование должны быть приняты на целевом железе в разделе 4.1.

5.2. Слои приложения

Пользовательский интерфейс и экраны
                ↓
Контроллер сессии и конечный автомат операции
                ↓
Сервис протокола контроллера
                ↓
IHardwarePort
       ↙                  ↘
MockHardwarePort     SerialPortHardware
                            ↘
                    UsbSerialHardware (stub)

UI не должен знать о SerialPort, потоках и формате строк протокола. Он реагирует на типизированные состояния и события контроллера сессии.

5.3. Состояния пользовательской сессии

Home
  ├→ CashAccepting
  │    → Dispensing
  │    → Completed
  └→ CardAmountSelection
       → CardCustomAmount (опционально)
       → CardTerminalWait
       → Dispensing
       → Completed
  → Home

Любое активное состояние
  → PaymentDeclined, Error или Cancelled
  → Home

Переход в Completed разрешён только после подтверждения выдачи контроллером. Сам факт отправки DISPENSE не считается успешной выдачей.

5.4. Сцены и глобальные сервисы

  • Главная сцена: src/scenes/App/App.tscn.
  • App создаёт два CanvasLayer: фон с layer = 0 и UI с layer = 1.
  • Экраны — отдельные атомарные сцены Control со своим C#-скриптом и локальными ресурсами.
  • ThemeManager и ScreenManager регистрируются как Autoload, как требует ТЗ.
  • SessionController, адаптер протокола и аппаратный сервис принадлежат главной сцене или отдельному композиционному узлу приложения; их не следует без необходимости превращать в глобальные Singleton.
  • ScreenManager переключает сцены экранов и не содержит бизнес-логику оплаты или выдачи.
  • SessionController является единственным владельцем состояния текущей операции.
  • HomeScreen сразу показывает цену, запас, способы оплаты, промо-fallback и контакты поддержки.
  • Платёжные сцены разделяются по назначению: CashPaymentScreen, CardAmountScreen, CardCustomAmountScreen и CardTerminalScreen.
  • AdvertisementPlayer является пассивным UI-компонентом домашнего экрана и останавливает декодирование, когда экран скрыт.

5.5. Формат темы

В Exchanger существует одна программная fallback-тема. Разрабатываемые темы полностью вынесены в соседний ExchangerThemeDlcBuilder и подключаются только как один внешний Godot .pck. После загрузки runtime-представление VendingThemeDefinition содержит:

  • стабильный идентификатор и отображаемое имя темы;
  • Theme для Control-интерфейса;
  • необязательную статическую Texture2D фона;
  • необязательные PackedScene фоновых слоёв/декораций и именованных пассивных визуальных слотов экранов;
  • для manifest v2 — полный набор из девяти пассивных визуальных сцен экранов с каноническими именами и metadata binding-id;
  • fallback-цвет; облегчённый режим хранится в конфигурации приложения.

Сам фон всегда статичен: ColorRect с fallback-цветом и, при наличии, неподвижный TextureRect. Анимированные спрайты темы добавляются отдельной PackedScene; такая сцена может содержать AnimatedSprite2D и AnimationPlayer, но обязана быть пассивной декорацией и не зависеть от экранов приложения. Фоновые шейдеры, частицы и видео не используются. Промо-ролик допускается только внутри отдельного UI-блока AdvertisementPlayer на HomeScreen.

Полносценовая тема не содержит бизнес-логику: приложение инстанцирует собственные C#-контроллеры, до _Ready заменяет встроенный визуальный корень сценой DLC и разрешает кнопки, поля и динамические области через стабильную metadata exchanger_binding_id. Вложенность узлов темы свободна. Manifest v1 сохраняет обратную совместимость как стилевой пакет; v2 требует все девять сцен целиком и точную копию каталога из 481 обязательного биндинга. Host закрепляет SHA-256 каталога и до активации отклоняет неверные пути, root-имена, неизвестные или неверно типизированные binding-id и дубли. Отсутствующий обязательный binding восстанавливается по каноническому FallbackPath узлом старой сцены либо готовым элементом встроенной темы-заглушки.

В основном проекте нет каталога themes/ и исходных ресурсов тем. Путь к единственному внешнему DLC берётся из пользовательского appsettings.json; пакет имеет версионированный manifest, изолированное пространство res://exchanger_theme_dlc/, монтируется без замены ресурсов приложения и требует перезапуска для обновления.

5.6. Потоки и последовательный порт

  • Только реализация SerialPortHardware работает с System.IO.Ports.
  • Чтение выполняется в отдельном потоке с корректной отменой и освобождением ресурсов.
  • Фоновый поток не вызывает API Godot и не обращается к Node/Resource/UI.
  • Принятые строки и изменения соединения передаются в поток Godot через ConcurrentQueue либо CallDeferred.
  • Разбор строк протокола выполняется отдельно от транспорта и покрывается тестами.
  • Повтор команды выдачи после неизвестного результата запрещён без идемпотентного идентификатора операции или явного подтверждения, что первая команда не была принята.

5.7. Конфигурация

  • Встроенные безопасные значения: res://config/default_settings.json.
  • Пользовательские настройки: user://config/appsettings.json.
  • При первом запуске создаётся пользовательский файл на основе значений по умолчанию.
  • Ошибочный файл не должен ломать запуск: ошибка логируется, применяются безопасные значения по умолчанию.
  • Планируемые параметры: порт, baud rate, таймауты, путь и разрешение внешнего DLC темы, режим имитации оборудования, уровень эффектов, общая громкость и громкость промо-ролика, mute, параметры логирования, цена жетона, карточные суммы, бонусные правила, бинарное mock-состояние запаса, способы оплаты, телефон поддержки и плейлист промо-роликов.
  • Секреты платёжного терминала в конфигурации приложения не хранятся; платёжная интеграция находится на стороне внешнего контроллера.

5.8. Критические эксплуатационные ограничения

  • Godot может включить borderless fullscreen и перехватывать обычные действия приложения, но не может надёжно запретить Alt+Tab, Ctrl+Alt+Del или системные жесты Windows. Полная блокировка обеспечивается Windows Assigned Access/Shell Launcher, политиками ОС и учётной записью киоска.
  • Тач-ввод принимает только первое касание (Index == 0). Дополнительные касания игнорируются централизованно.
  • Минимальная интерактивная область — 64×64 px, основной текст — не меньше 24–32 px на целевом разрешении.
  • В интерфейсе нет функций, доступных только по hover.
  • Целевой UI проектируется в портретном 9:16 с нативным viewport 720×1280; панели большего разрешения используют масштабирование canvas_items/expand.

5.9. Требования из визуальных референсов

  • Корпус — высокий напольный автомат с зелёной рамой, портретным экраном, физическими приёмниками справа и лотком выдачи ниже экрана.
  • Основной стиль — пиксельная космическая тема: тёмно-синий фон, салатовые акценты, белые панели, НЛО и зелёный маскот.
  • Домашний экран сразу показывает цену жетона, остаток, выбор наличной/карточной оплаты, действие выдачи, промо-ролик и контакты поддержки.
  • Наличная оплата отображает накопленный баланс и инструкции для купюро- и монетоприёмника.
  • Карточная оплата содержит пресеты сумм, ввод другой суммы, ожидание терминала и отдельный результат.
  • Бонусные жетоны рассчитываются конфигурируемыми правилами, а не зашиваются в UI.
  • Референсные файлы не являются production-ассетами; права, финальные исходники, тарифы и бренд требуют подтверждения.

6. Целевая структура каталогов

res://
├── config/
│   └── default_settings.json
├── src/
│   ├── core/
│   │   ├── Configuration/
│   │   ├── Logging/
│   │   ├── ThemeManager/
│   │   ├── Navigation/
│   │   └── Session/
│   ├── hardware/
│   │   ├── Abstractions/
│   │   ├── MockHardware/
│   │   ├── SerialPortHardware/
│   │   ├── UsbSerialHardware/
│   │   └── ControllerProtocol/
│   ├── scenes/
│   │   ├── App/
│   │   ├── HomeScreen/
│   │   ├── CashPaymentScreen/
│   │   ├── CardAmountScreen/
│   │   ├── CardCustomAmountScreen/
│   │   ├── CardTerminalScreen/
│   │   ├── SuccessScreen/
│   │   └── ErrorScreen/
│   ├── ui/
│   │   ├── StaticBackground/
│   │   ├── AdvertisementPlayer/
│   │   ├── TokenStockStatus/
│   │   └── TouchInputGuard/
│   └── vfx/
│       └── SuccessEffect/
└── tests/
    ├── Unit/
    ├── Integration/
    └── Fixtures/

Структура уточняется по мере реализации, но сохраняется правило атомарности: сцена, скрипт и специфичные ресурсы элемента лежат в одной тематической папке.

7. План реализации

Этап 0. Базовые решения и гигиена репозитория — завершён

Цель: получить однозначную техническую основу до реализации UI и оборудования.

  • Зафиксировать продуктовые требования в ROADMAP.md.
  • Зафиксировать исходный Compatibility renderer как исторический baseline.
  • Обновить корневой README.md: назначение, требования, запуск, структура, конфигурация и состояние проекта.
  • Удалить ошибочную корневую лицензию GodotX Toast, сохранив лицензии сторонних аддонов; собственную лицензию пока не объявлять.
  • Инициализировать локальный Git-репозиторий; подключение remote и первый коммит оставить отдельным решением владельца.
  • Решить хранение проектных инструкций: .agents/AGENTS.md версионируется и не исключается через .gitignore.
  • Добавить файл архитектурных решений docs/architecture/decisions.md.
  • Зафиксировать границу протокола: до появления документации wire-формат и команды не выдумываются; документация является входом Этапа 3, реальный COM — Этапа 4.
  • Принять универсальную аппаратную стратегию: не привязывать UI, конфигурацию и бизнес-логику к отсутствующему целевому ПК/GPU/тач-контроллеру.
  • Отложить выбор модели контроллера и проверку реального COM-обмена до добавления владельцем списка команд и документации в references/.
  • Зафиксировать контроллер единственным источником сведений об оплате, запасе, приёмниках, выдаче и аппаратных ошибках; конкретные сообщения определит будущая документация.

Критерий выхода выполнен: стабильны платформонезависимая граница IHardwarePort, универсальные требования к UI и правило не проектировать неизвестный протокол. Аппаратно-зависимые детали оформлены как входные данные Этапов 3–4 и не блокируют UI, mock-сценарии и бизнес-логику ранних этапов.

Этап 1. Первый вертикальный срез: запуск, тема и стартовый экран — завершён

Цель: получить запускаемое полноэкранное приложение с первой темой и безопасной заглушкой оборудования.

1.1. Настройки приложения

  • Установить базовый viewport 1920×1080 и режим растяжения canvas_items/expand.
  • Включить borderless fullscreen для production-профиля.
  • Предусмотреть удобный оконный debug-режим через конфигурацию или аргумент запуска.
  • Сохранить исходный Compatibility renderer как исторический baseline.
  • Настроить ограничение/целевую частоту 60 FPS и поведение при потере фокуса.
  • Добавить главную сцену App и назначить её в application/run/main_scene.
  • Проверить запуск на 1920×1080 и другом соотношении сторон: 1024×768, а также fullscreen 2560×1440.

1.2. Конфигурация и логирование

  • Реализовать типизированную модель AppSettings с валидацией диапазонов.
  • Реализовать загрузку defaults → пользовательский JSON → аргументы запуска.
  • Добавить безопасное восстановление после битого/неполного JSON.
  • Реализовать журнал ключевых событий с временными метками и категориями.
  • Добавить ротацию/ограничение размера файлов в user://logs.
  • Не записывать в лог платёжные секреты и лишние персональные данные.

1.3. Тема, статический фон и анимированные спрайты

  • Создать VendingThemeDefinition.
  • Создать базовый UI Theme со шрифтами, размерами, отступами и состояниями кнопок.
  • Создать первую тему cosmic.
  • Реализовать ThemeManager Autoload; первоначальный реестр позднее заменён моделью fallback + один DLC.
  • Исключить жёсткие пути к теме из экранов.
  • Применять одну активную тему ко всем экранам; горячая смена позднее удалена в пользу замены DLC при перезапуске.
  • Создать StaticBackground: обязательный ColorRect и необязательный статический TextureRect.
  • Передавать fallback-цвет и статическую текстуру из определения темы.
  • Поддержать отдельную тематическую сцену анимированных 2D-спрайтов через PackedScene.
  • Отключать необязательные анимированные спрайты в облегчённом режиме, сохраняя статический фон.
  • Удалить из активной темы фоновые шейдеры и частицы как не соответствующие референсам.

1.4. StartScreen и аппаратная заглушка

  • Создать атомарную сцену StartScreen с логотипом и кнопкой «Начать».
  • Обеспечить области касания не меньше 64×64 px и читаемый текст.
  • Временная debug-кнопка смены темы была реализована для прототипа и позднее полностью удалена.
  • Создать IHardwarePort без зависимости от Godot UI.
  • Создать MockHardwarePort, который логирует команды и воспроизводит ответы по сценарию.
  • Создать UsbSerialHardware как явно неподдерживаемый Android stub.
  • Создать каркас SerialPortHardware, не открывая реальный порт при старте сцены.
  • Проверить наличие System.IO.Ports: сборка отсутствует в установленных reference/shared packs .NET.
  • Получено разрешение устанавливать необходимые NuGet-пакеты; System.IO.Ports будет добавлен только при фактическом начале Этапа 4.

Критерии приёмки этапа:

  • приложение запускается в главную сцену без ошибок;
  • стартовый экран корректен в fullscreen и debug-window режимах;
  • единая активная тема применяется к UI и фону; позднее источником стал fallback либо один внешний DLC при запуске;
  • fallback-фон включается настройкой;
  • mock получает тестовую команду, а UI не зависит от конкретного транспорта;
  • повторная C#-сборка и запуск из Godot проходят без ошибок.

Этап 2. Портретный интерфейс, навигация и жизненный цикл сессии — завершён

Цель: перестроить технический прототип под портретный аппарат из референсов и реализовать детерминированные переходы без реального оборудования.

  • Перевести viewport и debug-window в портретный режим; после сверки с theme-preview закрепить нативную сетку 720×1280.
  • Реализовать ScreenManager Autoload и контейнер экранов в App.
  • Загружать экраны через централизованный ScreenRegistry, а не из разрозненных жёстких путей.
  • Сохранить общий статический фон между экранами и добавить короткие fade-переходы только для UI.
  • Блокировать ввод на время перехода полноэкранным TransitionInputBlocker; устранить перенос отпускания тача на новый экран.
  • Заменить StartScreen на HomeScreen с ценой, запасом, двумя способами оплаты, промо-fallback и контактами поддержки.
  • Создать CashPaymentScreen с балансом, расчётом жетонов/бонуса и инструкциями физических приёмников.
  • Создать CardAmountScreen с конфигурируемыми пресетами сумм.
  • Создать CardCustomAmountScreen с цифровой клавиатурой, ограничениями и подтверждением.
  • Создать CardTerminalScreen с ожиданием, отменой и обратным отсчётом.
  • Создать SuccessScreen с режимами Dispensing и Completed.
  • Создать ErrorScreen с отдельными визуальными состояниями отказа оплаты, связи, запаса и выдачи.
  • Реализовать AdvertisementPlayer с локальным видео, циклом, fallback-постером и остановкой на скрытых экранах.
  • Добавить модели PricingSettings, BonusRule, PaymentPreset, BrandingSettings и AdvertisementSettings.
  • Добавить компонент TokenStockStatus только с состояниями Enough/Low/Unknown, без числового остатка и Empty.
  • Реализовать SessionController и явные состояния сессии.
  • Реализовать общий таймаут бездействия 30–60 секунд для платёжных экранов.
  • Реализовать отдельный таймаут карточного терминала 120 секунд.
  • Реализовать возврат после успеха через 10 секунд.
  • Сбрасывать выбранную сумму/способ оплаты, таймеры и временные данные при возврате в Home.
  • Централизованно фильтровать мультитач и принимать только первое касание.
  • Защитить навигацию, платёжные действия и выдачу от повторного тапа, переноса отпускания и повторной команды.
  • Восстанавливать безопасный HomeScreen после неизвестного состояния сессии.

Критерии приёмки этапа:

  • весь сценарий проходится на mock без реального COM-порта;
  • назад, отмена и каждый таймаут приводят к определённому состоянию;
  • быстрые повторные касания не создают две сессии и не дублируют выдачу;
  • все экраны используют текущую тему и не владеют бизнес-логикой.
  • портретный UI соответствует композиции референса, нативно рендерится в 720×1280 и масштабируется на панели 9:16;
  • промо-ролик не продолжает декодироваться вне HomeScreen и имеет рабочий fallback.

Этап 3. Протокол контроллера и симулятор оборудования — завершён

Цель: отделить строковый протокол от транспорта и проверить все ветки сценария.

Условие входа выполнено: владелец проекта добавил в references/ документацию контроллера 2.3.5 и список команд. Этап закрывает документированный wire-профиль и безопасное поведение для отсутствующих форматов. Production-команда силового канала, физические номера входов и проверка AES на плате относятся к последующей аппаратной интеграции и не подменяются предположениями.

  • Изучить спецификацию универсального контроллера 2.3.5 и описать поддерживаемую часть в docs/hardware/PERIPHERY_CONTROLLER_2_3_5.md.
  • Реализовать ASCII-фрейминг по CR/LF с лимитом буфера, минимальной длиной и timeout 100 мс.
  • Реализовать типизированные команды авторизации, оплаты, отмены, status-запросов и legacy-хоппера; отсутствующий production-профиль силового канала представлен явным безопасным Unsupported.
  • Реализовать типизированные ответы startup-баннера periphery controller ver_<version>, CHAL, RAES, AUTH_PC_OK, AUTH_CNT_OK, точного READY!, MONEY:*, HOPPER1 APPEND/OK/ERROR/CREDIT/PULSE, совместимые ответы второго хоппера, SERVICE, INPUT и обобщённые ошибки; недокументированные status/heartbeat-ответы безопасно классифицируются как неизвестные.
  • Привязать подтверждённые входы новой прошивки: active-low IN8 публикует сервисный запрос по одному фронту, IN1 задаёт бинарный запас первого хоппера, IN4 — второго. IN24 сохранён как физический датчик первого канала, но runtime считает подтверждения строками платы; оба канала 30X/40X включены и остаются конфигурируемыми.
  • Добавить бинарное состояние запаса Enough/Low из реального INPUT<bitmask> (вариант с пробелом тоже принимается) без вымышленного точного количества; до первого снимка либо при отключённом входе используется Unknown.
  • Добавить события принятой наличной суммы и обязательные подтверждения ASK:COINS/BILL.
  • Добавить запуск/отмену Вендиста, keep-alive и Approved по MONEY:CASHLESS; несовпадение суммы даёт Declined, а Cancelled/Timeout воспроизводятся доменным mock и таймером сессии, поскольку отдельных wire-сообщений в спецификации нет.
  • Связать действия с аппаратными последовательностями: наличные 162 → 141 → 151, остановка наличных 142 → 152, подтверждение карточной суммы 142 → 152 → 161 → 163<amount>, отмена/таймаут карты 162; повторная отмена не засоряет FIFO одинаковыми командами.
  • Обработать частичные строки, несколько сообщений в одном чтении, timeout, переполнение, не-ASCII и неизвестные кадры.
  • Добавить доменную таблицу типизированных ошибок и безопасных текстов; документированные hopper/transport/controller ошибки сопоставлены, неизвестные коды сворачиваются в безопасный Unavailable без утечки сырого кадра.
  • Сырые строки оборудования и внутренние исключения не передаются в пользовательский интерфейс.
  • Mock покрывает успех, отказ, отмену, таймаут, disconnect, полную/частичную ошибку и задержку; PeripheryControllerSimulator дополнительно прогоняет настоящий handshake/parser, malformed-кадр, оплату, датчик и legacy-выдачу без COM-порта.
  • Добавлен debug-only режим ручной эмуляции оплаты, потери связи и ошибок выдачи.
  • Зафиксировать at-most-once-политику выдачи: адаптер подавляет повторную команду и не выполняет retry после обрыва или ошибки; новая явная операция разрешена без перезапуска приложения.
  • Для обоих хопперов отправлять пакеты 309N/409N без разделителя (1–99 жетонов), считать APPEND=N подтверждением очереди и ждать N соответствующих кадров OK; после ERROR дождаться CREDIT=N активного канала и вернуть частичный результат. Большие выдачи разбиваются и маршрутизируются по раздельным учётным остаткам; 3090/4090 запрашивают остаток кредита.

Критерий выхода выполнен: все ветви конечного автомата воспроизводимы детерминированно без физического аппарата, parser/authenticator/simulator не зависят от Godot Node, а unsupported-возможности не активируются по предположениям.

Этап 4. Реальный Windows COM-порт — завершён

Цель: подключить аппаратный транспорт без нарушения потоковой безопасности Godot.

  • Реализовать открытие/закрытие SerialPort с port name и параметрами протокола 115200 8N1/None.

  • Реализовать отдельный worker-поток чтения/записи и механизм отмены.

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

  • Публиковать события соединения и кадры только в главном потоке Godot через ProcessPendingEvents().

  • Реализовать последовательную очередь отправки команд с окончанием CRLF.

  • Добавить read/write timeout и периодическую попытку повторного открытия порта с конфигурируемым интервалом.

  • Не выполнять автоматический повтор выдачи, если результат первой команды неизвестен.

  • Корректно отменять worker, закрывать и освобождать порт при выходе/перезапуске.

  • Программно обрабатывать исчезновение USB-COM и повторное открытие устройства; логика и 200 циклов симуляции проверены, физическая приёмка кабеля вынесена в раздел 4.1.

  • Не журналировать сырые кадры и секрет handshake; неизвестные/developer-кадры логируются только по категории.

  • Добавить атомарный сервисный индикатор Disconnected/Authenticating/Ready/Faulted: в production штатный Ready скрыт, а обрыв и ошибка видимы без раскрытия COM-порта и протокольных данных.

  • Провести автоматический стресс-тест 200 последовательных переподключений с обрывом во время выдачи; команда не повторяется, отложенное подтверждение не принимается, переходы состояния не накапливаются.

  • Подготовить отдельный аппаратный NUnit-набор для платы: safe handshake/status/input/reconnect, ручные SERVICE/cash/sensor, отдельно подтверждаемые real payment, hopper и USB-cable сценарии с TRX-отчётом. Критерии приёмки этапа:

  • ни один фоновый поток не обращается к Godot API;

  • отключение кабеля не замораживает UI;

  • реконнект не создаёт лишних потоков и подписок;

  • выдача не дублируется после таймаута или обрыва связи;

  • приложение корректно закрывает порт.

Этап завершён: транспорт, потоки, reconnect, at-most-once и безопасное освобождение порта реализованы и проверены автоматически; ранее COM3/framing/AES-handshake, INPUT, безопасные status-запросы и три reconnect-цикла подтверждены на плате. Полная физическая приёмка автомата ведётся отдельно по разделу 4.1 и не является незавершённой программной задачей этапа.

Этап 5. Полный MVP-сценарий автомата — завершён

Цель: интегрировать оплату, выдачу, обратную связь и пользовательские состояния.

  • При выборе способа оплаты создавать уникальный локальный контекст операции и запись в журнале.
  • Реализовать расчёт жетонов по цене и бонусным правилам для наличной и карточной веток.
  • Блокировать начало оплаты при Unknown; разрешать при Enough и Low, не имитируя проверку несуществующего точного остатка.
  • Для наличной ветки обновлять баланс по типизированным событиям и явно определять момент начала выдачи.
  • Для карточной ветки передавать выбранную сумму аппаратной границе и различать Approved/Declined/Cancelled/Timeout.
  • На экране ожидания терминала разделить действия: верхняя кнопка «Назад» отменяет активный безналичный запрос и возвращает к выбору суммы, красная «ОТМЕНА» завершает сессию.
  • Перейти в ожидание оплаты и принять только релевантный сигнал текущего состояния.
  • Игнорировать просроченное подтверждение от предыдущей mock-сессии.
  • После подтверждения оплаты один раз сформировать команду выдачи.
  • Поддержать крупные тарифы: общее количество выдаётся последовательными пакетами 309N, следующий пакет отправляется после N кадров HOPPER1 OK предыдущего.
  • Показывать состояние «Идёт выдача», не преждевременный успех.
  • Обновлять прогресс/счётчик по каждому подтверждённому контроллером импульсу выдачи.
  • Показывать «Получите жетоны» только после подтверждения завершения.
  • Сопоставить ошибки с типизированными безопасными действиями ReturnHome/TryAnotherPaymentMethod/ContactSupport.
  • Не показывать действие «Повторить» после ошибок: экран ошибки разрешает только возврат домой, смену способа оплаты либо обращение в поддержку и никогда не повторяет выдачу.
  • Логировать начало и единственный итог с локальным OperationId без платёжных секретов; отсутствующий в протоколе transaction ID не эмулируется и не используется для небезопасной сверки.
  • Выполнить автоматический сброс после успеха и управляемый сброс после ошибки.
  • При старте закрывать незавершённые локальные записи как UnknownAfterRestart без автоматического retry; при отсутствии документированного запроса статуса операция не повторяется.

Критерий готовности MVP выполнен программно: полный сценарий от Home до подтверждённой выдачи проходит через типизированный mock и детерминированный wire-симулятор, а известные ошибки завершаются безопасным и понятным состоянием. Сквозная физическая проверка на автомате вынесена в раздел 4.1.

Этап 6. Киоск-режим, настройки и эксплуатационная надёжность — завершён

Цель: подготовить приложение к работе без постоянного оператора и дать авторизованному сервисному персоналу безопасный способ изменять эксплуатационные параметры без ручного редактирования JSON.

  • Настроить borderless fullscreen и скрытие курсора в production.
  • Подготовить инструкцию Windows kiosk/shell setup для блокировки системных действий.
  • Добавить автоматический запуск приложения после входа/загрузки Windows.
  • Добавить watchdog/перезапуск процесса средствами ОС.
  • Добавить обработку потери фокуса, смены разрешения и состава дисплеев.
  • Не позволять пользовательскому вводу открывать debug/service функции: F10 работает только в debug-сборке из Home, production-вход начинается только с аппаратного SERVICE.
  • Реализовать защищённый PIN-вход в сервисный режим с лимитом попыток и временной блокировкой; единый PIN включается и впервые задаётся внутри приложения, в конфигурации хранится только PBKDF2-хеш с солью.
  • Исключить из настроек владельца версию, тему/DLC, порт и контроллер, окно/FPS, диагностику, журналы и тестовую выдачу; технические параметры оставить только в deployment-конфигурации.

6.1. Редактор настроек программы

  • Провести инвентаризацию AppSettings и разделить параметры на операторские, инженерные, debug-only и секретные; AES-ключ, платёжные секреты и сырые команды контроллера никогда не показывать и не сохранять через UI.
  • Ввести единый SettingsService/репозиторий конфигурации: экраны работают с валидированной рабочей копией и неизменяемым активным снимком, а не записывают JSON напрямую.
  • Создать атомарный экран настроек владельца внутри защищённого режима с пятью разделами: «Общие», «Звук», «Промо», «Тарифы» и «Оплата».
  • Добавить общую громкость 0–100%, mute и отдельную громкость промо-ролика; применять значения через выделенные Godot audio buses и сохранять их в пользовательской конфигурации.
  • Добавить безопасную кнопку проверки звука, которая не запускает оплату, выдачу или полноразмерный промо-ролик.
  • Сделать редактируемой стоимость одного жетона TokenPriceRubles; показывать рядом предварительный расчёт количества жетонов для типовых сумм и запрещать нулевое/отрицательное значение.
  • Реализовать редактор дополнительных жетонов BonusRules: строки «минимальная сумма → бонусные жетоны», добавление, удаление и изменение правил, сортировка порогов, запрет дублей и отрицательных значений.
  • Явно зафиксировать в UI семантику бонусов: применяется одно максимальное подходящее правило, бонусы разных строк не суммируются; до сохранения показывать итог «базовые + бонусные» для контрольных сумм.
  • Реализовать настройку карточных пресетов: добавление/удаление сумм, сортировка, отсутствие дублей, максимальная сумма и шаг ручного ввода; валидировать суммы относительно цены жетона и правил PricingPolicy.
  • Добавить отдельное включение/отключение наличной и карточной оплаты; не позволять сохранить конфигурацию, в которой отключены оба пользовательских способа оплаты.
  • Сделать настраиваемыми пользовательские таймауты бездействия, ожидания оплаты и показа результата в безопасных пределах; рядом объяснить влияние каждого параметра.
  • Добавить редактирование названия автомата, подзаголовка и телефона поддержки с ограничениями длины и preview итогового текста.
  • Добавить настройки промо: включение, список промо-роликов, poster, fallback-текст, mute и громкость; проверять существование и допустимость локальных путей до сохранения.
  • Добавить экранный ввод для настроек: RU/EN/спецсимволы для текстовых полей, отдельную цифровую клавиатуру для SpinBox, Shift, каретку, удаление/очистку и кнопку скрытия справа сверху; автоматически поднимать активное поле над клавиатурой и применять число при выходе.
  • Оставить путь к внешнему DLC, ReducedEffects, журналирование, параметры окна, mock-режим, COM-порт и соединение только в deployment-конфигурации, полностью исключив их из UI владельца.
  • Выделить аппаратную deployment-конфигурацию в отдельный user://config/technical_settings.toml: основной порт, безопасные параметры связи, наличие обоих хопперов, их входы/полярности и сервисная кнопка валидируются целиком и применяются только после перезапуска; при повреждении пользовательского файла используется встроенный mock-профиль.
  • Не давать изменять тарифы, бонусы, способы оплаты, тему и аппаратные параметры во время активной операции; открытая сессия всегда завершается со снимком настроек, с которыми началась.
  • Реализовать валидируемое автосохранение с debounce 0,5 с: промежуточный некорректный ввод не записывается, ошибки остаются рядом с полями, а повторный фронт IN8 сохраняет ожидающие допустимые изменения и закрывает настройки. Экранная кнопка «Сохранить и выйти» в неподвижном нижнем футере использует тот же безопасный путь завершения редактирования.
  • Сохранять appsettings.json атомарно через временный файл и замену, перед изменением держать последнюю корректную резервную копию и автоматически восстанавливаться после повреждения.
  • Валидировать весь набор настроек до записи и показывать ошибки рядом с конкретным полем; частичное применение некорректной конфигурации запрещено.
  • Разделить параметры на применяемые немедленно и требующие перезапуска; после автосохранения показывать понятный статус и не выполнять самопроизвольный restart во время операции.
  • Добавить безопасный журнал изменения конфигурации: дата/время, роль, раздел и факт изменения без старого/нового секретного значения, полного пути, платёжных данных и сырых кадров контроллера.
  • Покрыть нормализацию, граничные значения, round-trip JSON, миграцию версии, резервную копию, бонусные правила и запрет изменения активной операции unit/integration-тестами.
  • Провести runtime-проверку редактора владельца на 1080×1920 и 720×1280: пять доступных вкладок, отсутствие технических панелей, тач-действия, PIN-клавиатура и длинные/некорректные значения; собственные текстовая и цифровая клавиатуры проверяются в fallback и внешней теме, а работа пальцем и в перчатках остаётся физической приёмкой Этапа 8.

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

6.2. Эксплуатационная надёжность

  • Добавить очистку/ротацию старых логов и ограничение объёма user://.
  • Проверить восстановление после повреждения конфигурации, обрыва питания во время записи и несовместимой версии файла без потери последней корректной резервной копии.
  • Добавить ускоренную модель 24 часов (1440 минут, 480 выдач), стресс 10 000 жизненных циклов SessionFlow и процессный runner для 24-часового прогона; runner проверен минутным прогоном 12/12 сэмплов, пик working set 217.73 МБ. Реальный 24-часовой прогон переносится в физическую приёмку Этапа 8, поскольку целевой ПК пока отсутствует.

Этап 7. Производительность, UX и доступность — выполняется

Цель: обеспечить стабильные 60 FPS и понятный интерфейс на целевом оборудовании.

  • Локальный baseline FPS, CPU-времени, памяти и draw calls зафиксирован (docs/operations/PERFORMANCE_BASELINE.md); повтор на автомате вынесен в раздел 4.1.
  • Проверить статический fallback-фон и актуальный space-pixel в полном/облегчённом режимах: decoration переключается 1 → 0 → 1, сохраняются 60 FPS, draw calls снижаются с 26 до 24.
  • Перевести production-темы на доверенный runtime: разрешить theme-owned GDScript и стандартные Godot-узлы без прежнего decoration budget, лимитов узлов/кадров/FPS/скорости или размера PCK; сохранить строгую проверку namespace, manifest, экранов, биндингов и запрет подмены ресурсов host.
  • Отключать обработку всех скрытых экранов через ProcessMode.Disabled и включать её только для текущего экрана; переход Home → Settings проверен в runtime.
  • Добавить явную прокрутку Settings перетаскиванием одним пальцем и зажатой ЛКМ; обработчик использует deadzone 12 px, не перехватывает короткое нажатие и не зависит от того, передал ли вложенный Control drag штатному ScrollContainer. Физическая приёмка остаётся в 4.1.
  • Проверить контраст normal/pressed/disabled/error/success во fallback и space-pixel; CheckButton дополнительно получил тёмный текст и встроенные крупные ON/OFF-индикаторы.
  • Убрать зависимость от hover и мелкие интерактивные элементы: все девять экранов пройдены runtime-аудитом, цифровые клавиатуры имеют равные крупные колонки, логика не использует hover.
  • Синхронизировать runtime-композицию с девятью preview-сценами space-pixel: перевести host на нативную сетку 720×1280, перенести геометрию панелей и подключить безопасные именованные visual slots.
  • Перевести DLC-контракт на manifest v2 с полными визуальными сценами всех девяти экранов, app-owned контроллерами, стабильными element binding-id и обратно совместимым v1 fallback.
  • Реорганизовать ExchangerThemeDlcBuilder в themes/example и themes/<theme-id>, добавить валидатор имён/типов/биндингов и preview-overlay, показывающий binding-id каждого элемента.
  • Адаптировать host к самодостаточным production-сценам Builder: проверять metadata, собственный Theme/фон/decoration, единый режим всех девяти экранов и отсутствие дублирующих глобальных слоёв; при ReducedEffects скрывать и отключать обработку только BackgroundDecoration, не ограничивая остальную доверенную runtime-логику темы.
  • Запретить host переоформлять самодостаточные сцены: Settings больше не назначает внешним узлам WhitePanel/семантические variations, keypad-контроллеры не меняют их размеры и стили, ThemeManager.ApplyTo оставляет Theme/геометрию полноэкранного view нетронутыми.
  • Убрать runtime-достраивание самодостаточных сцен: Home, карточные суммы и изменяемые списки Settings работают с готовыми узлами и фиксированными слотами темы; host только подключает события и передаёт функциональные значения, не добавляя и не удаляя Control-узлы.
  • Добавить в Settings theme-owned текстовую и цифровую клавиатуры: 57 новых биндингов, состояния preview OFF/TEXT/NUM, неизменяемое визуальное дерево и общую C#-логику поля ввода; каталог DLC расширен до 460 обязательных биндингов.
  • Добавить владельцу раздел «Сервис» с отдельным атомарным учётом остатка, ручным добавлением через двойное подтверждение и списанием только фактически подтверждённых выдач.
  • Реализовать автоматический пересчёт прямыми командами 301/300 или 401/400: считать только HOPPER1/2 PULSE=1, не дублировать учёт по INPUT, завершать после 10 секунд тишины, позволять исправить результат перед записью и безопасно останавливать мотор при отмене/закрытии.
  • Расширить self-contained theme-контракт сервисными и четырьмя состояниями индикатора запаса до 477 элементов; template и space-pixel содержат готовые Service-панель и диалог, host меняет только значения и события.
  • Проверить деградацию при временной просадке FPS: таймауты используют монотонное реальное время и завершаются корректно даже при единственном обновлении после скачка на 120 секунд.

Программные ограничения, профили эффектов и локальный baseline реализованы. Проверка критерия 60 FPS, панели и декодера промо на автомате ведётся централизованно в разделе 4.1.

Этап 8. Тестирование и контроль качества — выполняется

Цель: сделать регрессии воспроизводимыми до установки на автомат.

  • Выбрать C# test runner: NUnit 4 для чистого доменного ядра и проверок файлов проекта.
  • Конфигурация, тарифы, валидация суммы, переходы SessionFlow, framing, parser и handshake покрыты unit-тестами.
  • Добавить интеграционные тесты SessionFlow + IMachineController test double; production MockMachineController дополнительно проверять в Godot runtime.
  • Добавить автоматическую проверку наличия/заголовков всех сцен, валидатор manifest DLC и runtime smoke-загрузку каждого экрана через Godot Editor.
  • Добавить тесты таймаутов с управляемым источником времени, без реального ожидания 120 секунд.
  • Проверить duplicate action, повторный DISPENSE, поздние события, disconnect/reconnect и неизвестные/malformed wire-ответы.
  • Проверить полные наличный и безналичный wire-сценарии от действия сессии до ASK:*, выключения приёмников и выдачи; отдельно проверить пакет 515 жетонов как пять 30999 и один 30920, подтверждённые соответствующим числом HOPPER1 OK.
  • Проверить отсутствие накопления подписок и стабильность многократных последовательных сессий.
  • Проверять C#-сборку и NUnit-набор после каждого завершённого блока.
  • Проверять Godot editor/game logs после изменений сцен и ресурсов.
  • Составить ручной чек-лист приёмки на физическом аппарате. Физические проверки панели, SERVICE, дисплея и 24-часовой soak вынесены в раздел 4.1.

Этап 9. Сборка, установка и обновление Windows-версии — выполняется

Цель: получить воспроизводимый устанавливаемый релиз.

  • Создать export preset Windows x64 и воспроизводимый staging-сборщик.
  • Проверить включение self-contained .NET runtime, GodotSharp.dll и System.IO.Ports.dll.
  • Явно исключить references/, тесты, документацию, инструменты и dev-only addons из production-экспорта; в проверенной сборке нет test DLL и addon-файлов.
  • Проверить res:// и user:// коротким headless smoke-run экспортированного EXE (exit code 0).
  • Исправить one-EXE runtime: встроенные JSON/TOML материализуются через Godot FileAccess, config/*.toml включается в PCK, а runtime-autoload GodotxToast не удаляется export-фильтром.
  • Проверить итоговый one-file release: каталог artifacts/windows/test_q содержит ровно один Exchanger.exe размером 190229328 байт; packaged mock и COM3 smoke завершились без ошибок.
  • Определить расположение пользовательской конфигурации и логов для сервисного персонала.
  • Добавить номер версии сборки в UI сервисного режима и журналы.
  • Подготовить сценарий установки, обновления и отката.
  • [~] Добавить цифровую подпись: текущий EXE подписан самоподписанным сертификатом CN=Robotail с timestamp DigiCert; остаётся определить production-модель доверия и установить доверенный корень на целевые устройства либо использовать сертификат доверенного CA.
  • Зафиксировать контрольную версию конфигурации контроллера и приложения.

Чистая установка и проверка релиза на целевом устройстве вынесены в раздел 4.1.

Этап 9.1. Linux x86_64 для Debian 13 — выполняется

Цель: перенести desktop runtime на Debian без изменения UI, бизнес-логики и wire-протокола.

  • Добавить Godot export preset Linux x86_64 (Debian 13) с production-фильтрами Windows-пресета.
  • Встроить PCK и managed build outputs; актуальный self-contained ELF x86_64 имеет размер 155351952 байта, RID linux-x64 и native System.IO.Ports.
  • Проверить заголовок артефакта: ELF64, AMD64/x86_64.
  • Разрешить в технической конфигурации /dev/ttyUSB*, /dev/ttyACM* и стабильные /dev/serial/by-id/*, сохранив строгую валидацию Windows COM-портов.
  • Собрать отдельный Linux Debug ELF с расширенной безопасной диагностикой UART и handshake, не перезаписывая release-артефакт.
  • Подготовить udev/dialout и доступ сервисного пользователя к USB-UART.
  • Подготовить Linux kiosk/autostart/watchdog и процедуру обновления/отката. Физический запуск и приёмка на Debian 13 вынесены в раздел 4.1.

Этап 10. Подготовка Android-порта после MVP

Цель: заменить платформенный транспорт без переработки UI и бизнес-логики.

  • Выбрать и согласовать USB-Serial Android-плагин.
  • Реализовать UsbSerialHardware за существующим IHardwarePort.
  • Добавить запрос USB permission и обработку переподключения устройства.
  • Проверить ограничения Android kiosk/lock task mode.
  • Адаптировать пути конфигурации/логов и жизненный цикл pause/resume.

Физическая Android-приёмка целевого ARM64-устройства вынесена в раздел 4.1.

8. Сквозные требования к реализации

  • Все новые скрипты по возможности пишутся на C#.
  • Скрипты Godot Node/Resource объявляются как partial class.
  • Комментарии в C# ведутся единообразно; для проекта выбран русский язык документации и пояснений сложной логики.
  • Экраны не открывают порт, не парсят аппаратные строки и не управляют потоками.
  • Ресурсы экранов и компонентов хранятся атомарно в соответствующих папках.
  • Пути тем и экранов централизованы в реестрах/конфигурации.
  • Сигналы и подписки освобождаются при завершении жизненного цикла узла.
  • Таймауты и переходы состояний должны быть детерминированы и тестируемы.
  • Новые внешние зависимости и NuGet-пакеты добавляются только при фактической необходимости; разрешение владельца на необходимые NuGet-пакеты получено, назначение и версия каждой зависимости фиксируются в документации.
  • После существенных изменений обновляются этот ROADMAP, документация и agentmemory.

9. Основные риски и меры снижения

Риск Последствие Мера снижения
Неопределённый протокол контроллера Переделка транспорта и сценария выдачи Зафиксировать протокол и реальные примеры обмена до интеграции COM
Повтор DISPENSE после обрыва Двойная выдача жетонов Transaction ID/статус операции; запрет слепого retry
Поздний ответ от старой сессии Выдача не тому сценарию Идентификатор и поколение сессии, сброс входного контекста
UI обращается к Godot из COM-потока Падения и гонки Queue/CallDeferred, строгая граница транспорта
Слабый GPU Просадки FPS и задержки тача Compatibility, лимиты эффектов, fallback-фон, профилирование
Ожидание, что Godot заблокирует Alt+Tab Выход пользователя из киоска Настройка Windows Assigned Access/Shell Launcher
Несовместимый или повреждённый DLC темы UI не может загрузить внешние ресурсы Версионированный .pck, строгий manifest и программная fallback-тема
Неограниченные логи Заполнение диска Ротация, лимит размера и срока хранения
Android-зависимости проникают в UI Дорогой порт IHardwarePort и platform-specific composition
Неясный результат операции при аварии Финансовая/операционная ошибка Журнал транзакций, запрос статуса, безопасный сервисный сценарий
Текущий UI остаётся ландшафтным Несоответствие реальному корпусу Первым шагом Этапа 2 перейти на 1080×1920 и провести визуальную QA 720×1280
Промо-ролик декодируется всегда Лишняя нагрузка и просадки FPS Останавливать плеер вне HomeScreen, ограничить формат/разрешение, иметь постер
Референсные цифры приняты за спецификацию Неверные тарифы и бонусы Хранить всё в конфигурации и запросить подтверждение владельца
Референсные ассеты попали в релиз без прав Лицензионный риск references/ в .gitignore; исключить каталог в export preset и использовать только подтверждённые production-исходники

10. Вопросы для владельца продукта

  • [?] Подтвердить цену жетона: 10 рублей в референсе или другое production-значение.
  • [?] Подтвердить таблицу бонусов, включая пример «100 + 50 в подарок».
  • [?] Подтвердить карточные пресеты 10/50/100/150/200/300/400/500 рублей и предел другой суммы.
  • [?] Есть ли выдача сдачи/возврат наличных и что происходит при отмене наличной операции?
  • [?] Что именно запускает кнопка «Выдать» на домашнем и наличном экранах?
  • [?] Кто является источником подтверждения оплаты и в каком формате приходит событие?
  • [?] Может ли контроллер подтвердить фактический результат выдачи, включая частичную выдачу?
  • [?] Поддерживает ли протокол идентификатор транзакции и запрос статуса последней операции?
  • [?] Что должен делать автомат после подтверждённой оплаты и частичной/неуспешной выдачи?
  • [?] Нужны ли возврат средств или обращение к оператору, и кто их выполняет?
  • [?] Требуется ли локальное хранение истории операций и каков срок хранения?
  • [?] Нужны ли удалённая диагностика, телеметрия или выгрузка логов?
  • Сервисный вход открывается по аппаратному SERVICE; опциональный единый PIN настраивается в приложении при первом входе, матрица разрешений зафиксирована в docs/operations/SETTINGS_ACCESS_MATRIX.md.
  • [?] Подтвердить финальные бренд, логотип, телефон, персонажа и права на референсные материалы.
  • [?] Уточнить форматы промо-роликов, плейлист, звук и способ обновления контента.
  • [?] Требуется ли локализация кроме русского языка?
  • [?] Какой максимально допустимый простой и кто отвечает за автоматический перезапуск?

11. Ближайший рабочий пакет

Этапы 0–6 закрыты. Этапы 7, 8, 9 и 9.1 остаются в работе. Все проверки, требующие физического автомата, собраны в разделе 4.1. Ближайший программный пакет:

  1. Подготовить установку, обновление и откат Windows-релиза.
  2. Определить production-модель доверия цифровой подписи.
  3. Зафиксировать контрольную пару версий приложения и прошивки.
  4. Подготовить udev/dialout и доступ сервисного пользователя к USB-UART в Debian 13.
  5. Подготовить Linux kiosk/autostart/watchdog и процедуру обновления/отката.

Аппаратная очередь не дублируется здесь и полностью ведётся в разделе 4.1.

12. Журнал прогресса

2026-09-07 — Формат «К ВЫДАЧЕ» унифицирован для наличной и карточной оплаты

  • Готовая и ручная карточная сумма показывают базовые жетоны слева и бонус отдельно справа: + N ЖЕТОНОВ\nВ ПОДАРОК либо БЕЗ БОНУСА; длинная объединённая строка больше не используется.
  • Заголовок БАЛАНС ЖЕТОНОВ на экране наличной оплаты заменён на К ВЫДАЧЕ.
  • Для отдельного бонуса CardAmount добавлен supplemental runtime binding с побиндинговым fallback; immutable JSON-каталог сохраняет 481 ID и прежний SHA-256. Документация Theme Builder и обе внешние темы синхронизированы отдельно.
  • Debug-сборка проходит без предупреждений, 487/487 NUnit-тестов, встроенная тема и свежие PCK проходят headless PaymentRewardFormattingSmoke на mock-оборудовании без обращения к COM/UART.
  • Предыдущий установленный space-pixel также проходит compatibility-smoke: отсутствующий новый CardAmount Bonus подключается как один встроенный fallback binding без отказа всего DLC.
  • Builder contract/validator/preview/PCK smoke и визуальный QA 720×1280 прошли для обеих тем. Новые пакеты: example.pck — 464876 байт, SHA-256 02B8505FB7F0EF4F4E9C2905D97E8F8751A1DDE86DDF460AFC958CCE6AF51994; space-pixel.pck — 1436988 байт, SHA-256 4D9AA00FE2096794331B348916D1766B47A45D9EDFEE60BEAB0E424ED4761A67. Пользовательский active_theme.pck не заменялся.

2026-09-07 — Учёт жетонов и ограничение покупки разделены

  • В разделе «Сервис» появились два включённых по умолчанию переключателя: ведение точного учёта и независимый запрет покупки сверх остатка с учётом бонуса. При отключении самого учёта скрываются точные остатки, кнопки пересчёта/добавления и приблизительное число на Home; сохранённый файл остаётся для последующего восстановления режима.
  • Если учёт включён, а ограничение выключено, остатки остаются видимыми и продолжают списываться, но не блокируют оплату. При включённом ограничении карточная сумма отклоняется до запроса терминалу, наличная — до команды выдачи; пресеты и ручной ввод показывают тот же лимит.
  • Выключенный режим не списывает сохранённый остаток; DualHopperAuto использует hopper 1 и сохраняет последовательное разбиение по 99 без автоматического failover.
  • Конфигурация повышена до версии 23. Новые элементы темы оформлены в Builder как supplemental runtime bindings; immutable JSON-каталог остаётся на 481 ID с прежним SHA-256.
  • Debug/Release проходят 483/483 NUnit-теста. Встроенная тема и предыдущие example.pck/ space-pixel.pck прошли отдельный runtime-smoke без реального COM/UART; проверка на автомате добавлена в раздел 4.1. Старые PCK получают новый переключатель побиндинговым fallback.
  • Свежие Builder-пакеты прошли contract/validator/preview/PCK smoke, визуальную проверку 720×1280 и host runtime-smoke без fallback-подстановок: example.pck — 464268 байт, SHA-256 AC03334354D285E36A25BFB81D60880F829700F083AF5D728F10AB15C764B63D; space-pixel.pck — 1436492 байта, SHA-256 166660916913E4766FC91437155BB8B78A405150B3A599CF401CD78A83DFB727. Установленный пользовательский active_theme.pck не заменялся.

2026-09-07 — Упрощён раздел общего текста в настройках

  • Поле «Название» скрыто, «Короткий текст» переименован в «Свой текст», отдельный branding-preview удалён из отображения раздела «Общие».
  • Branding.ApplicationName сохранён в модели и JSON для обратной совместимости, но больше не редактируется и не может скрытой ошибкой заблокировать сохранение формы владельца.
  • Binding ID, пути, типы и fallback не менялись: legacy-узлы остаются скрытыми контрактными слотами, поэтому версия и SHA-256 binding-каталога не изменяются.
  • Встроенная сцена, example и space-pixel синхронизированы. Debug/Release unit-наборы прошли по 460/460; host runtime-smoke прошёл для встроенной темы и нового space-pixel.pck, а обе DLC-темы прошли Builder contract/validator/preview/PCK smoke и визуальную проверку 720×1280.
  • Новые пакеты: example.pck — 463324 байта, SHA-256 25E90059652EC26BD1779F7D6761F1CD1248A458F8C93F8E242596D3988FDE9F; space-pixel.pck — 1435548 байт, SHA-256 E5941A4E7437BA996945E85551714F8B6E11F77A64808C68B29A8B88F66629AB. Активная пользовательская тема автоматически не заменялась.

2026-09-04 — Сниженная высота питомца оставлена только для sitting

  • Theme-owned компонент space-pixel сохраняет локальную поправку (0,0) для sitting и применяет (0,-24) ко всем другим анимациям. Root, navigation, hit box и GROUND_OFFSET=(64,96) не меняются.
  • Builder verifier подтвердил переход (0,0) → (0,-24) → (0,0) и неподвижность root; contract, validator, preview smoke, build и PCK verifier прошли. Preview 720×1280 учитывает union-envelope состояний и подтвердил Nearest без clipping/пересечения с заголовком.
  • Полный host InteractivePetBehaviorSmoke с явным новым PCK и mock-оборудованием прошёл: drag/drop, поверхности и production-loader сохранили прежнее поведение.
  • Новый PCK: 1435452 байта, SHA-256 390C9E3F83B026304F0E75E5A6AAD4458684BBCDFC847FFB542B6CFB1AC278E9; активная пользовательская тема автоматически не заменялась.

2026-09-04 — Питомец space-pixel увеличен на 30%

  • Theme-owned RUNTIME_SCALE изменён с 0.65 на 0.845; host-код и сцены Exchanger не переделывались. Точка лап и hit-area масштабируются существующей общей формулой.
  • Builder contract/test/validator/preview smoke/build и PCK verifier прошли; preview 720×1280 подтвердил Nearest без clipping. Новый PCK: 1435004 байта, SHA-256 60E41BA7C1043348AB7C3FEED72C352AE3FBFA58BDACB00F4036F1CD0EB56088.
  • Production-loader Exchanger и полный InteractivePetBehaviorSmoke с явным новым PCK прошли на mock-оборудовании: drag/drop, поверхности, смена сцен и четыре танца сохранены.

2026-09-04 — Физическая сервисная кнопка сохранена поверх клавиатуры

  • Блокировка «Сохранить и выйти» ограничена её экранным обработчиком и GUI hit-test футера. Аппаратный ServiceRequested по-прежнему напрямую вызывает TryFinishEditing().
  • Повторное физическое нажатие при открытой клавиатуре закрывает её, завершает сохранение и возвращает Home по прежнему маршруту; незавершённые сервисные операции всё ещё блокируют выход.
  • Runtime-smoke воспроизводит физическое нажатие через общий debug-action при открытой клавиатуре на встроенной и установленной DLC-темах. Debug/Release без предупреждений, NUnit — 459/459.

2026-09-04 — Футер больше не блокирует нижний ряд экранной клавиатуры

  • Установлено, что Button.Disabled не исключает кнопку и родительскую панель из hit-test, поэтому перекрывающая геометрия фиксированного футера перехватывала физические нажатия нижнего ряда.
  • Пока клавиатура открыта, футер и «Сохранить и выйти» используют MouseFilter.Ignore; после её закрытия восстанавливаются исходные фильтры, а обработчик выхода остаётся защищён отдельно.
  • Runtime-smoke проверяет реальный GUI hit-test нижних клавиш обеих панелей и координатный клик текстовой клавиши. Встроенная и установленная DLC-темы прошли; Debug/Release без предупреждений, NUnit — 458/458.

2026-09-04 — Ввод экранной клавиатуры применяется без задержки

  • Исправлена рассинхронизация, при которой вставленный экранной клавиатурой текст был виден в LineEdit, но не попадал в рабочую копию Settings до первого удаления.
  • Каждая операция вставки, удаления, очистки и замены выделения теперь формирует ровно одно уведомление TextChanged; числовое поле дополнительно немедленно применяет значение SpinBox.
  • Runtime-smoke контролирует событие после первой буквы и после каждой введённой цифры на встроенной и установленной DLC-темах. Debug/Release: 0 предупреждений, NUnit — 458/458.

2026-09-04 — Выход из Settings заблокирован поверх экранной клавиатуры

  • OnScreenKeyboardController сообщает об открытии и закрытии любой своей панели; фиксированная кнопка «Сохранить и выйти» отключается синхронно с этим состоянием.
  • Обработчик выхода отдельно проверяет фактическую видимость клавиатуры и игнорирует программное нажатие, даже если состояние Button.Disabled было обойдено.
  • Runtime-smoke проверяет текстовую, обычную цифровую и сервисную цифровую клавиатуры на встроенной и установленной DLC-темах. Debug/Release собраны без предупреждений, NUnit — 458/458.

2026-09-04 — Переподключение платы переведено на технический интервал 30 секунд

  • Подтверждено, что авторитетный параметр уже находился в [controller].reconnect_interval_seconds; шаблон и активная локальная конфигурация изменены с 5 на 30 секунд, остальные монтажные параметры не затронуты.
  • При отсутствии платы после первой немедленной попытки порт повторно открывается каждые 30 секунд. После обрыва работавшего транспорта отсчёт начинается заново от момента обнаружения обрыва, поэтому устаревший стартовый дедлайн больше не вызывает немедленный reconnect.
  • Планировщик повторно ставит дедлайн после каждой неудачной попытки; оплату и выдачу не повторяет. Оба сценария покрыты детерминированными unit-тестами без COM/UART.
  • Debug/Release собраны без предупреждений, NUnit — 458/458; headless mock подтвердил фактическую загрузку reconnectSeconds=30 из текущего пользовательского TOML.

2026-09-04 — Ручной ввод суммы получил однозначное удаление цифры

  • Левая кнопка нижнего ряда CardCustomAmount переименована из «ОТМЕНА»/«СБРОС» в «СТЕРЕТЬ» и удаляет только последнюю введённую цифру; после удаления последней цифры экран показывает 0.
  • Legacy binding clear_button сохранён ради совместимости, а отдельный обязательный backspace_button скрыт, чтобы в интерфейсе не было двух одинаковых действий.
  • Runtime-smoke подтвердил последовательность 12 → 1 → 0 на старом установленном PCK; Debug/Release собраны без предупреждений, NUnit — 455/455.
  • Обе темы прошли Builder validator/preview/build/PCK verify. В space-pixel надпись полностью помещается при 720×1280; новый PCK прошёл production-loader приложения на mock-оборудовании.

2026-09-04 — В Settings добавлен фиксированный футер сохранения

  • Кнопка «Сохранить и выйти» вынесена из прокручиваемого содержимого в закреплённый нижний футер; drag по футеру не прокручивает список, а нижний зазор позволяет показать последние поля над ним.
  • Кнопка завершает ожидающее атомарное автосохранение через общий TryFinishEditing() и возвращает Home только после успеха; физическая сервисная кнопка сохраняет прежнее поведение.
  • Пока открыта текстовая или цифровая экранная клавиатура, кнопка отключена; обработчик также отклоняет программное нажатие, чтобы клавиатура не могла случайно инициировать выход.
  • Для новых DLC добавлены supplemental bindings футера, кнопки и нижнего зазора. Старые PCK получают только эти отсутствующие элементы из встроенной темы-заглушки.
  • Встроенная тема, ранее установленный PCK и новый полный space-pixel.pck прошли runtime-smoke на mock-оборудовании; Debug/Release собраны без предупреждений, NUnit — 454/454.

2026-09-04 — Добавлена побиндинговая обратная совместимость DLC-тем

  • Отсутствующий обязательный binding больше не отключает весь DLC: runtime сначала принимает совместимый узел старой темы по каноническому FallbackPath, а при его отсутствии переносит готовый элемент встроенной темы-заглушки вместе с её оформлением.
  • Если точечно собрать экран невозможно, встроенным становится только этот экран; остальные корректные сцены внешней темы сохраняются. Неизвестные ID, дубли, неверные типы, root и повреждённый контракт по-прежнему отклоняются.
  • Реальный ранее установленный space-pixel.pck принят без пересборки: Home восстановил один, Settings — четыре новых биндинга. Запуск прошёл на mock без обращения к COM/UART.
  • Release-сборка прошла без ошибок и предупреждений, NUnit — 452/452.

2026-09-04 — В сервис добавлено скрытие элементов домашнего экрана

  • В новой панели «Скрытие элементов меню» независимо управляются учётный остаток жетонов, theme-owned персонаж справа и весь промо-блок; старые настройки мигрируют с показом всех трёх.
  • Изменения автосохраняются и применяются к уже созданному Home без перезапуска приложения.
  • Host меняет только видимость готовых узлов fallback/DLC. Скрытые остаток и промо исключаются вместе с маршрутами питомца, поэтому он не садится на невидимые поверхности.
  • Обе темы прошли Builder validator/preview-smoke и PCK verifier; новый space-pixel.pck дополнительно загружен production loader приложения на mock-оборудовании без ошибок.
  • Release-сборка прошла без ошибок и предупреждений, NUnit — 448/448.

2026-09-03 — Debug-сборка получила расширенную диагностику подключения платы

  • Добавлен компилируемый только в Debug уровень [DEBUG]; Release не содержит вызовов и строк расширенной диагностики.
  • Журнал фиксирует платформу и параметры UART, открытие устройства, reset DTR/RTS, запуск и завершение worker, безопасную причину транспортной ошибки, reconnect и переходы handshake.
  • Сырые кадры, AES challenge/response, ключи и платёжные payload не выводятся; показываются только тип этапа, состояние, длина и безопасные счётчики.
  • Runtime summary в разделе «Диагностика» Debug-сборки показывает transport/auth state.
  • Debug и Release собраны без ошибок и предупреждений, обе конфигурации прошли NUnit 444/444; Debug headless-smoke на mock подтвердил фактическую запись [DEBUG] в журнал.
  • Собран отдельный Linux Debug ELF: 155630576 байт, SHA-256 A1FB92126D6ED753DF968180075D7409678E5F0A7C454CB6D2B954DC024682B6.

2026-09-03 — Последовательный порт адаптирован для Windows и Linux

  • Общая валидация выбирает правила по фактической ОС процесса: Windows принимает только COM1…COM256, Linux — /dev/ttyUSB*, /dev/ttyACM* и /dev/serial/by-id/*.
  • При use_mock = true имя порта не проверяется и безопасный общий шаблон с COM3 остаётся пригодным для mock-запуска Linux-сборки.
  • SerialPortHardware остался единым desktop-транспортом на System.IO.Ports; сообщения транспорта больше не привязаны к термину COM.
  • Unit-тесты отдельно покрывают обе платформенные ветки и недопустимые Linux-пути.
  • Release-сборка прошла без ошибок и предупреждений, NUnit — 444/444.
  • Linux release повторно экспортирован одним ELF64 AMD64-файлом размером 155351952 байта, SHA-256 7312E79514842EB5EA3193604F71CAAE412654957640032B62D66E202802284C.

2026-09-02 — Экранная клавиатура подключена к ручному пополнению хоппера

  • При открытии диалога «Добавить в хоппер» поле количества после появления модального слоя автоматически получает фокус, выделяет текущее значение и показывает цифровую клавиатуру.
  • Контроллер клавиатуры учитывает, что сервисное поле находится вне основного ScrollContainer, и не пытается прокручивать скрытый список настроек.
  • Сцены темы не менялись: host использует существующие binding-id поля, диалога и готовой theme-owned цифровой клавиатуры.
  • Runtime-smoke открывает сервисный диалог, проверяет видимость клавиатуры и фокус поля, затем закрывает оба слоя. Fallback и space-pixel.pck прошли с mock-оборудованием.
  • Сборка прошла без ошибок и предупреждений, NUnit — 410/410; 18 hardware-тестов пропущены.

2026-09-02 — Выдача двух хопперов переведена на строгий приоритет первого

  • Для запроса N план выдачи теперь всегда равен H1=min(N, остаток H1), H2=N-H1: второй хоппер используется только для недостающей разницы после первого.
  • Удалён прежний выбор стартового хоппера по датчикам уровня и сравнительному остатку, из-за которого выдача могла начинаться со второго хоппера.
  • При недостаточном суммарном учётном остатке ни одна команда выдачи не отправляется. Аппаратная ошибка во время уже начатой выдачи по-прежнему не вызывает автоматический retry/failover.
  • Mock приведён к тому же правилу и больше не создаёт фиктивное подтверждение сверх остатка.
  • Интеграционный тест подтверждает последовательность H1=6, H2=20, запрос 10: сначала 3096, после шести HOPPER1 OK — 4094, итоговый раздельный учёт (6, 4).
  • Для обоих хопперов отдельно закреплена последовательная пакетная выдача 205 жетонов: 30999 → 30999 → 3097 либо 40999 → 40999 → 4097. APPEND=N не запускает следующий пакет; он отправляется только после всех 99 OK. Смешанный план 180 при остатках (150, 50) даёт 30999 → 30951 → 40930, не запуская второй хоппер раньше завершения первого.
  • Сборка прошла без ошибок и предупреждений, NUnit — 409/409 (18 hardware-тестов пропущены), runtime-smoke с space-pixel.pck и mock-оборудованием прошёл без binding-ошибок.

2026-09-02 — Исправлено склонение слова «жетон» в пользовательском потоке

  • Добавлен единый форматтер русских окончаний: 1 жетон, 2–4 жетона, 5–20 жетонов, включая исключение 11–14 и повторяющиеся разряды (21, 22, 111, 124).
  • Исправлены тексты выбора суммы, наличной оплаты, произвольной карточной суммы, ожидания терминала, прогресса выдачи и финального сообщения, в том числе количество подарочных жетонов.
  • Внешние сцены не изменялись: host обновляет только динамический текст существующих Label.
  • Сборка прошла без ошибок и предупреждений, NUnit — 395/395 (18 hardware-тестов пропущены), runtime-smoke с space-pixel.pck и mock-оборудованием прошёл без binding-ошибок.

2026-09-02 — Добавлен раздельный остаток жетонов по хопперам

  • Settings показывает точный общий остаток и отдельные значения для хоппера 1 и хоппера 2; данные берутся из сохранённого TokenInventorySnapshot и обновляются после подтверждённой выдачи, пересчёта и ручного добавления.
  • Host использует существующие theme-owned Hopper1Label/Hopper2Label; геометрия и оформление внешней темы приложением не меняются.
  • В контракт добавлены два Label-binding: всего 481 биндинг, SHA-256 каталога — DDDC89118BEEA78A820561464001656136ED42AC1AD5710FB109D25ADAF44143.
  • Сборка прошла без предупреждений и ошибок, NUnit — 376/376 (18 hardware-тестов пропущены). example и space-pixel прошли contract/validator/preview smoke; fallback и собранный space-pixel.pck прошли host runtime-smoke с mock-оборудованием. Сервисная панель space-pixel визуально проверена в Theme Builder при 720×1280 без наложений и обрезания текста.

2026-09-02 — Windows Debug и Release разведены по отдельным пресетам

  • Неоднозначный Windows x64 заменён на Windows x64 Release и Windows x64 Debug с разными output-путями, custom features, console wrapper и настройками debug symbols/C# sources.
  • tools/export-windows.ps1 принимает -Configuration Release|Debug, по умолчанию собирает Release и передаёт Godot соответствующий --export-release либо --export-debug.
  • Release создаёт Exchanger.exe, Debug — Exchanger.Debug.exe; каталоги содержат конфигурацию и timestamp, поэтому варианты не перезаписывают друг друга.
  • Оба режима фактически экспортированы официальным Godot 4.7.1 Mono и прошли packaged headless smoke с mock-оборудованием: Debug содержит основной EXE и console wrapper, Release — один EXE. Контрольные файлы не подписаны; Authenticode остаётся отдельным release-шагом.

2026-09-02 — Разделены таймауты наличной и безналичной оплаты

  • Конфигурация v20 получила независимые CashPaymentSeconds и CashlessPaymentSeconds; прежний PaymentSeconds мигрируется без потери значения, а наличный таймаут наследует прежний SessionIdleSeconds.
  • SessionFlow использует наличный таймаут на экране приёма денег и продлевает его при активности или изменении баланса; ожидание карточного терминала использует отдельный безналичный таймаут.
  • В Settings добавлены два самостоятельных поля с отдельной валидацией. Fallback, example и space-pixel содержат готовые theme-owned элементы; C# только подключает их по биндингам.
  • Host-контракт синхронизирован до 479 биндингов и одновременно закрыл прежний блокер card_terminal.content.top_bar.back_button; SHA-256 каталога — B33F34A50837560AF8C3589B3E0AA19BB03B7B534AF6A27001C4FA19DE4155BF.
  • Release/Debug собираются без предупреждений, NUnit — 372/372. Обе темы прошли contract, validator и preview smoke; установленный space-pixel прошёл Exchanger headless smoke с mock-оборудованием без binding-ошибок и без доступа к реальному COM.

2026-09-02 — CardTerminal «Назад» возвращает к выбору суммы

  • Host подключает существующий binding верхней кнопки BackButton, не изменяя сцену внешней темы.
  • Перед возвратом из CardTerminalWait вызывается CancelActiveOperation(), поэтому Вендист выключается и предыдущий результат оплаты игнорируется вне состояния ожидания терминала.
  • Красная кнопка «ОТМЕНА» сохраняет прежнее поведение: завершает карточную сессию и возвращает Home.
  • kiosk_back теперь соответствует верхней кнопке «Назад»; добавлен регрессионный тест перехода.
  • Release-сборка прошла с 0 ошибок/0 предупреждений, NUnit — 367/367; headless Godot smoke с --mock-hardware=true загрузил актуальную space-pixel без исключений и без обращения к COM3.

2026-09-01 — Этапы 4 и 5 закрыты, физическая приёмка централизована

  • Этап 4 закрыт по готовности COM-транспорта, worker-потока, reconnect и at-most-once; оставшиеся действия с реальным контроллером перенесены в раздел 4.1.
  • Этап 5 закрыт по фактической безопасной политике UI: кнопки повторной выдачи нет, ошибки ведут домой, к другому способу оплаты либо в поддержку, а UnknownAfterRestart не запускает retry.
  • Все актуальные проверки панели, производительности, платежей, обоих хопперов, пересчёта, USB-reconnect, soak и целевых платформ собраны в «4.1. Проверки на автомате» и удалены из этапов.
  • Ближайший рабочий пакет теперь содержит только программные и эксплуатационные задачи.

2026-09-01 — Дорожная карта синхронизирована с one-file export и текущим контроллером

  • Удалено устаревшее утверждение, что плата отключена: последний packaged Windows smoke успешно авторизовал COM3, однако новые аппаратные действия по-прежнему требуют отдельного разрешения.
  • Зафиксированы one-file Windows release, исправления embedded PCK/TOML/autoload и текущее состояние подписи.
  • Добавлен отдельный этап Linux x86_64 для Debian 13: export готов, целевой runtime, serial-path, udev/dialout, kiosk и физическая приёмка остаются открытыми.
  • Ближайший рабочий пакет синхронизирован с фактическими задачами Этапов 5, 7, 8, 9 и 9.1.

2026-08-31 — Включён полноценный учёт и выдача двух хопперов

  • Временный запрет второго хоппера отменён: целевой профиль DualHopperAuto использует 30X и 40X, подтверждения принимаются только от активного канала.
  • Точный остаток хранится отдельно для каждого хоппера; ручное добавление и автоматический пересчёт доступны раздельно, а подтверждённая выдача списывается с фактически сработавшего канала.
  • Покупателю на главном экране справа от полоски запаса показывается только приблизительный общий остаток: <100, шаг 100 до 1000 включительно и шаг 500 выше 1000.
  • Владельцу в разделе «Сервис» показывается точный учётный остаток с человекочитаемым склонением слова «жетон»; приблизительные диапазоны из интерфейса настроек удалены.
  • Главная индикация получила четыре состояния: пустая полоска «нет жетонов», красная 25% «мало», красная 50% «достаточно», зелёная 100% «много». Состояние вычисляется совместно по общему учёту и active-low датчикам IN1/IN4.
  • Внешний DLC-контракт дополнен предсозданными визуалами четырёх состояний; приложение только переключает их видимость и не перестраивает сцену темы.
  • Конфигурация поднята до версии 18 и мигрирует старые одноканальные профили в DualHopperAuto.
  • Обычный набор расширен тестами двух остатков, округления, правил индикации, 40X, второго пересчёта и двух датчиков. Аппаратные тесты и COM не запускались.

2026-08-31 — Интегрирован протокол новой прошивки первого хоппера

Историческая запись промежуточного состояния. Ограничение одного хоппера отменено записью выше.

  • На тот момент разводка: IN8 — сервисная кнопка; IN1/IN24 — запас и физическая выдача первого хоппера; IN4 — запас второго. Вход выдачи второго хоппера пока неизвестен.
  • До исправления прошивки второго канала runtime работает только в Hopper1Auto, отправляет только 30X и отклоняет Hopper2Auto без записи 40X в COM.
  • Выдача использует 309N, типизированные ответы HOPPER1 APPEND=N, HOPPER1 OK, HOPPER1 ERROR, HOPPER1 CREDIT=N и запрос остатка 3090. Ошибка не защёлкивает хоппер до перезапуска и не вызывает автоматический повтор команды.
  • Подтверждено ограничение одной команды: N = 1–99; 309100 возвращает ERROR:30:invalid count (0-99). Runtime использует общую константу 99 и разбивает крупную выдачу на последовательные пакеты.
  • Для будущего второго канала сохранён симметричный контракт 409100 → ERROR:40:invalid count (0-99); симулятор и тесты его знают, но runtime продолжает запрещать любые 40X.
  • Автоматический пересчёт запускается 301, считает только строки HOPPER1 PULSE=1 и останавливает мотор командой 300 после 10 секунд тишины.
  • Вендиста поддерживается командой 161 в idle/card-состоянии с интервалом 120 секунд, то есть заведомо чаще требуемых прошивкой 180 секунд; на время наличной оплаты и выдачи используется 162.
  • Конфигурация поднята до версии 17 и мигрирует прежний целевой Hopper2Auto на Hopper1Auto; на том этапе второй датчик выдачи сохранялся как -1, позднее владелец подтвердил его привязку к IN25.
  • Код, симулятор, unit-тесты, аппаратный runner и документация синхронизированы. После фиксации предела пакета обычный набор прошёл 323/323, решение собрано без предупреждений и ошибок. Аппаратные тесты и любые обращения к COM не выполнялись, потому что плата отключена.

2026-08-31 — Авторизация синхронизирована с актуальным plate_config_app

  • После открытия COM-порта выполняется аппаратный reset: очистка буферов, DTR=false, RTS=true на 100 мс, затем RTS=false.
  • Контроллер должен прислать точный startup-баннер periphery controller ver_<version>; после паузы 2000 мс выполняется 07 → CHAL → 08 → RAES.
  • Корректный RAES, AUTH_PC_OK и AUTH_CNT_OK являются промежуточными стадиями. Рабочие команды разрешаются только после точного кадра READY!.
  • Синхронизировать таймауты с актуальным plate_config_app: startup-баннер — 20 с, обязательная пауза — 2000 мс, CHAL и RAES — по 2000 мс, финальный READY! — 5 с. Конфигурация v19 переводит только старое стандартное значение 5000 мс на 2000 мс и сохраняет явно заданные пользовательские значения.
  • Входящие CHAL и RAES разбираются совместимо с plate_config_app: поддерживаются слитный HEX-payload, пробел или двоеточие после префикса.
  • Симулятор, unit/integration-тесты и безопасный аппаратный probe приведены к тому же контракту.
  • Решение собирается без предупреждений; программный NUnit-набор проходит 310/310.
  • Физический auth-probe на COM3 прошёл 1/1; Safe прошёл 8/9. Новый handshake, 91/IN1, 140, 150, 409?, 99 и три reconnect-цикла подтверждены.
  • Единственный открытый аппаратный результат — отсутствие ответа на 160 в двух независимых запусках. Это не блокирует авторизацию, но требует проверки подключения/режима Вендиста.

2026-08-28 — Исправлена пакетная выдача второго хоппера

  • Реальный обмен показал формат прошивки: 40910 ставит в очередь 10 жетонов, после чего плата отвечает HOPPER2 queued 10 prizes и десять раз HOPPER2 OK.
  • Hopper2Auto переведён с ошибочной схемы «одна 409 → опрос IN25» на 409N → N × HOPPER2 OK; короткие импульсы IN25 больше не могут быть пропущены периодическим опросом и не дублируют подтверждения платы.
  • Пакеты ограничены 99 жетонами; выдача 515 жетонов детерминированно разбивается на пять 40999 и один 40920, следующий пакет отправляется только после завершения предыдущего.
  • Parser типизирует HOPPER2 queued N prizes, симулятор воспроизводит реальный порядок кадров. Обычный набор прошёл 289/289; 17 аппаратных тестов безопасно пропущены без явного разрешения на запуск мотора.

2026-08-27 — Целевой хоппер исправлен на второй

  • Владелец уточнил фактическую разводку: IN1 и IN25 принадлежат второму хопперу, а IN2/IN24 — будущему первому.
  • Добавлен режим Hopper2Auto и типизированные HOPPER2 OK/ERROR. Первоначальная трактовка одиночной 409 и подсчёта IN25 позднее заменена подтверждённым пакетным форматом 409N; актуальное поведение описано в записи от 2026-08-28.
  • Status-запрос аппаратного runner переведён с исторического 309? на 409? для второго хоппера; формат ответа ещё нужно подтвердить безопасным прогоном COM3.
  • Конфигурация версии 15 автоматически мигрирует прежний Hopper1Auto на Hopper2Auto, если он использовал целевую пару IN1/IN25; локальный COM3-профиль уже обновлён.
  • Регрессии того блока покрывали прежнюю интерпретацию 409/IN25; после проверки прошивки они заменены тестами 409N, HOPPER2 queued и HOPPER2 OK. Актуальный набор и результаты указаны выше.

2026-08-27 — Платёжные действия повторно сверены с протоколом платы

  • Для наличной ветки зафиксирована и протестирована последовательность 162 → 141 → 151, обязательные ASK:COINS/BILL, а перед выдачей/при отмене — 142 → 152.
  • Для безналичной ветки выбор способа не включает терминал до подтверждения суммы; подтверждение отправляет 142 → 152 → 161 → 163<amount>, MONEY:CASHLESS сначала получает обязательный ASK:CASHLESS, затем терминал выключается 162. Несовпадающая сумма выдачу не запускает.
  • Платёжные каналы стали явно взаимоисключающими, а повторные Cancel/End не добавляют одинаковые команды в последовательную очередь платы.
  • Последующее уточнение разводки показало, что IN1/IN25 относятся ко второму хопперу; поэтому прежняя реализация 309/Hopper1Auto для целевого автомата была заменена на 409/Hopper2Auto в следующем блоке работ.
  • Полные cash/card сценарии и пакет 515 жетонов прошли через настоящий parser, authenticator, PeripheryMachineController, SessionFlow и wire-симулятор. Набор: 283/283 unit/integration; 17 аппаратных тестов по-прежнему требуют явных флагов, реальные деньги и мотор не запускались.
  • После изменений безопасная аппаратная группа повторно запущена на CP210x/COM3: 9/9, включая handshake, INPUT, активный IN1 → Low, 140, 150, 160, 309?, 99 и три reconnect-цикла. Отчёт: artifacts/board-tests/board-safe-20260827-161454.trx. Реальные деньги и мотор не запускались; один краткий UnauthorizedAccessException при переоткрытии восстановлен retry.

2026-08-27 — Настраиваемый короткий текст в речевом bubble

  • Добавлена отдельная настройка Branding.ShortText с понятным названием «Короткий текст», лимитом 48 символов, автосохранением, валидацией и preview.
  • HomeScreen подставляет значение только в авторский Label речевого bubble по биндингу home.scroll.content.speech_text; старый Subtitle и его биндинги не возвращаются.
  • Конфигурация поднята до версии 14: существующий профиль получает безопасный текст по умолчанию и атомарно сохраняется с резервной копией.
  • Theme Builder добавил новый HOME-биндинг и три SETTINGS-биндинга short_text; актуальный контракт содержит 460 ID и имеет SHA-256 1FA54C9B9A0A7DFF71A5FAD38A5E9036E6C2D8F3D0BD298F003524A3E752FA10.

2026-08-26 — Исправлена полярность датчика запаса IN1

  • Подтверждено по протоколу платы и утилите тестирования входов: цифровые входы используют pull-up, поэтому активный сигнал на IN1 сбрасывает бит маски.
  • Production-сопоставление исправлено на IN1 bit 0 → Low и IN1 bit 1 → Enough; обновлены defaults, аппаратные runner/tests и документация.
  • Конфигурация поднята до версии 13: профиль с подтверждённым входом IN1 автоматически мигрирует с ошибочной active-high полярности на active-low, а прежний JSON сохраняется в .bak.
  • На подключённой плате COM3 безопасная группа прошла 9/9; текущий активный IN1 был интерпретирован как Low. Модульные тесты прошли 262/262.

2026-08-26 — Короткое описание полностью удалено

  • Удалено поле короткого описания из модели BrandingSettings, нормализации, валидации, настроек владельца, preview и HOME.
  • Конфигурация поднята до версии 12: старое JSON-поле Subtitle игнорируется и удаляется при автоматической миграции с сохранением предыдущего файла в .bak.
  • Из контракта DLC и обеих тем были удалены четыре HOME/SETTINGS-биндинга короткого описания; на этом шаге контракт содержал 456 обязательных биндингов и имел SHA-256 E77F754AB2C3913928B7DF18DD3D8F3205000D1531C414CC5B621E21CDF03ECF.

2026-08-26 — Экранные клавиатуры настроек

  • В src/ui/OnScreenKeyboard добавлен общий C#-контроллер: русский и английский алфавиты, Shift, 33 спецсимвола, каретка, пробел, очистка/удаление и отдельный цифровой режим.
  • Все LineEdit и SpinBox Settings, включая динамические fallback-строки и фиксированные слоты внешней темы, регистрируются автоматически; native virtual keyboard отключён, числовой ввод подтверждается через SpinBox.Apply(), а активное поле поднимается над нижней панелью.
  • Fallback и обе темы содержат заранее созданные текстовую и цифровую панели с кнопкой скрытия справа сверху. Runtime не создаёт и не перестраивает их Control-узлы.
  • Theme Builder получил общий компонент, preview-состояния KEYBOARD: OFF/TEXT/NUM и 57 новых metadata binding-id. Контракт расширен с 403 до 460 биндингов; SHA-256 каталога: 4552F55F78E744DF90DC4D8153330D4A6A6C156673C12B384E828D99AA93CE3A.
  • NUnit-набор расширен до 262 тестов; fallback Settings импортируется и запускается headless без ошибок. Отдельный runtime smoke подтвердил ввод букв и числа, показ и скрытие обеих панелей как во fallback, так и в установленной внешней теме. Ручная проверка пальцем и в перчатках остаётся частью физической приёмки Этапа 8.
  • Установлен space-pixel.pck размером 672840 байт, SHA-256: 752710CB91BD7D4A5FCC60A80B427671B2E98CE937203CD24A5648943008399A. Предыдущий пакет сохранён как active_theme.pre-onscreen-keyboard.pck (634888 байт, SHA-256: 22359A8D488352BA3E2C5E6E294C6822EEDEA853BD66ADF429C861D6C7B00DD8).
  • У бонусного SpinBox удалена приписка «жет.» во fallback и во всех 16 фиксированных слотах обеих тем: многозначное количество дополнительных жетонов теперь показывается полностью. Установлен исправленный space-pixel.pck размером 672520 байт, SHA-256: 5CC976A4B8EB07A15571F00DCB74BD7D46F1CBC8BDEAF97E1A994131763321E7; предыдущий пакет сохранён как active_theme.pre-bonus-suffix-fix.pck.

2026-08-26 — Host адаптирован к самодостаточным сценам Theme Builder

  • Поддержана новая структура themes/example, themes/space-pixel и themes/<theme-id>; production-сцены теперь сами содержат Theme, статический ThemeBackground и BackgroundDecoration с metadata exchanger_self_contained_visuals = true.
  • Loader проверяет структуру каждой сцены, запрещает смешивать два режима внутри одного PCK и отклоняет глобальные background/decoration пути, которые дублировали бы экранную композицию.
  • ReducedEffects теперь скрывает и полностью останавливает decoration внутри текущего экрана; единый лимит узлов, CanvasItem, анимаций и кадров применяется и к самодостаточной decoration.
  • Устранено runtime-переоформление готовых сцен: прежний проход Settings принудительно назначал панелям WhitePanel и менял внешний вид относительно preview; теперь визуальные семантики и размеры keypad применяются только к встроенному fallback, а self-contained view сохраняется.
  • Root внешней сцены больше не переименовывается в ThemeView и не получает повторный layout preset; бюджет decoration стал чистой проверкой без изменения FPS/speed_scale ресурса.
  • Удалены семь зон runtime-достраивания: запас, связь и промо Home, кнопки карточных сумм, правила бонусов, суммы оплаты и список промо Settings. На этом шаге контракт был расширен до 403 обязательных биндингов; самодостаточная тема заранее содержит все визуальные элементы и фиксированные слоты.
  • Актуальный space-pixel.pck размером 632992 байта установлен в стандартный user://; SHA-256: 122A6F16ED727080AC1F8EC407E94CD5D9E6CDBDF0A0465F27E1AF6C26ADAF6E. Предыдущий пакет сохранён рядом как active_theme.pre-binding-only.pck; более ранняя копия также сохранена как active_theme.pre-self-contained.pck.
  • Solution собирается без предупреждений; 254 unit/integration-теста проходят, 17 физических hardware-тестов безопасно пропущены без явного запуска на плате.

2026-08-25 — Завершён полносценовый DLC-контракт v2

  • Все девять C#-контроллеров экранов отделены от визуального дерева: внешний DLC заменяет только SafeMargin на собственный ThemeView, а оплата, навигация, настройки и оборудование остаются в основном приложении.
  • Зафиксированы шаблон имён screen_<key>.tscn, root-имена ThemeScreen_<Name>, metadata exchanger_screen_binding_id и exchanger_binding_id; расположение элементов внутри темы больше не влияет на привязку.
  • В host добавлен закреплённый SHA-256 каталога из 187 обязательных биндингов. До активации PCK проверяются девять сцен, ID, Godot-типы, полнота, отсутствие неизвестных биндингов, дубли и полный запрет скриптов.
  • Обратная совместимость manifest v1 сохранена как стилевой режим с единственной встроенной fallback-темой.
  • ExchangerThemeDlcBuilder реорганизован в themes/_template и themes/space_pixel; preview использует реальные production-сцены, показывает 9 вкладок и overlay binding-id с live reload.
  • _template и space_pixel проходят full-scene validator. Итоговый PCK размером 407832 байта установлен в стандартный user://; SHA-256 пакета: CB1EC8F6C6F908139CD6B602E5A7A401E271F8AD5F69BF5C3943B4FD6D10FF47.
  • Runtime smoke подтвердил внешний ThemeView у всех девяти контроллеров, отсутствие app-owned Timer/AudioStreamPlayer внутри темы и чистый журнал Godot. Solution собирается без предупреждений; 247 unit/integration-тестов проходят, 17 физических hardware-тестов безопасно пропущены без явного запуска на плате.

2026-08-25 — Runtime-интерфейс синхронизирован с preview темы

  • Логический viewport приложения переведён с 1080×1920 на нативные для theme-builder 720×1280; пиксельный текст больше не проходит через промежуточное уменьшение 2/3.
  • Главная, наличная и карточная ветки, терминал, успех, ошибка и сервисный вход получили геометрию, панели, кнопки и общий footer из соответствующих preview-сцен space_pixel.
  • DLC manifest v1 обратно совместимо расширен именованными VisualSlots для пассивной графики home, cash, card и result; host сохраняет поверх них собственные тексты и бизнес-логику.
  • Для decoration добавлен явный BackgroundDecorationScale; текущий фон 1080×1920 масштабируется до нативной сетки коэффициентом 0.666667 без влияния на screen-specific слоты.
  • Настройки владельца пересчитаны в нативные координаты 720×1280, при этом пять разрешённых вкладок и автоматическое сохранение сохранены; технические разделы не возвращались.
  • Актуальный space_pixel PCK собран и установлен. Сборка без предупреждений; 234/234 модульных теста прошли, 17 аппаратных тестов безопасно пропущены без явного запуска на плате.

2026-08-25 — Настройки переориентированы на владельца автомата

  • Из навигации SettingsScreen удалены вкладки «Тема», «Оборудование» и «Диагностика»; в шапке оставлен только короткий заголовок «НАСТРОЙКИ» без ролей и поясняющего подзаголовка.
  • Убраны внешний DLC и путь .pck, сводка применения темы, технический итог промо, COM/mock/reconnect, окно/FPS, состояние приложения, предупреждения, журналирование и тестовая выдача.
  • Владельцу оставлены пять разделов: «Общие», «Звук», «Промо», «Тарифы» и «Оплата», включая PIN, оформление главного экрана, цены, бонусы, способы оплаты и время ожидания.
  • Скрытые deployment-параметры исключены из валидации формы владельца и больше не могут заблокировать автосохранение доступных полей.
  • Runtime QA на 720×1280 подтвердила пять вкладок, понятные формулировки и отсутствие всех технических панелей; сборка без предупреждений, 228/228 модульных тестов прошли, 17 аппаратных безопасно пропущены.
  • Удалены избыточные пояснения о назначении проверочного звука и общем диапазоне карточных сумм; высота соответствующих карточек и шапки уменьшена без пустых зон.
  • Тексты всех доступных владельцу разделов и экрана ввода PIN переписаны простым языком: убраны термины fallback, «таймаут», «порог», «конфигурация» и технические формулировки автосохранения; пример тарифа теперь показывает готовое количество жетонов с правильным склонением.
  • В space_pixel и встроенной резервной теме полностью отключены тени текста и обводка кнопок; светлый текст переведён в чистый белый, а текст на светлых и зелёных поверхностях — в более тёмный контрастный оттенок. Обновлённый PCK собран и установлен в user://theme_dlc/active_theme.pck.
  • Исторические записи ниже описывают прежний инженерный UI на момент его реализации и считаются заменёнными этой записью.

2026-08-24 — PIN перенесён в настройки приложения

  • Удалена зависимость сервисного входа от EXCHANGER_SERVICE_OPERATOR_PIN и EXCHANGER_SERVICE_ENGINEER_PIN.
  • На вкладке «Общие» добавлены включение защиты и сброс PIN; изменения по-прежнему сохраняются автоматически.
  • Первый вход после включения или сброса предлагает дважды ввести 4–12 цифр, затем открывает инженерные настройки; последующие входы проверяют PIN и блокируются на 30 секунд после пяти ошибок.
  • В конфигурации версии 11 сохраняются только случайная 16-байтовая соль, PBKDF2-SHA256-хеш длиной 32 байта и 210000 итераций; открытый PIN не попадает в JSON, audit или логи.
  • Одинаковая политика применяется к физическому IN0 и debug-действию F10; при выключенной защите они открывают настройки напрямую.
  • Runtime-проверка прошла полный цикл создания, подтверждения и повторной проверки PIN; тестовая пользовательская конфигурация после проверки восстановлена. Сборка без предупреждений, 225/225 модульных тестов прошли, 17 аппаратных тестов безопасно пропущены без явного профиля запуска.

2026-08-24 — Цифровые входы привязаны к хопперу

  • Добавлена именованная production-разводка: IN0 — active-low сервисная кнопка, активный IN1 (бит 0) — мало жетонов, IN25 — active-low импульс выдачи. Позднее владелец уточнил, что эта пара относится ко второму хопперу; IN2/IN24 зарезервированы для первого.
  • IN0 публикует одно событие только при переходе в активный уровень; debug-сборка открывает Settings напрямую, release-сборка использует защищённый путь ServiceAccess → Settings, а следующий фронт закрывает настройки после автосохранения. При настроенном входе IN0 дублирующий кадр SERVICE игнорируется, поэтому один физический нажим не выполняет два перехода.
  • IN1 обновляет Enough/Low только при реальном изменении и никогда не создаёт числовой остаток.
  • IN25 считает один жетон на active-low фронт только во время активной выдачи. В целевом Hopper2Auto кадры HOPPER1 игнорируются; без исходного снимка выдача блокируется безопасной ошибкой.
  • Быстрые входы резервно опрашиваются командой 91 каждые 100 мс; индексы, полярности и интервал вынесены в конфигурацию. Конфигурация версии 13 исправляет production-полярность на active-low: IN1 bit 0 → Low, IN1 bit 1 → Enough.
  • Безопасная проверка COM3 прошла 9/9; текущий IN1=0 корректно преобразован в Enough. Мотор, платёжные приёмники и банковский терминал не запускались.
  • Сборка завершилась с 0 предупреждений и ошибок; модульный набор прошёл 212/212.

2026-08-24 — Handshake сверен с Unity, Python и реальной платой

  • На дату записи Unity использовался как источник криптографической проверки RAES. Позднее актуальный plate_config_app стал источником обязательной startup-последовательности с баннером, паузой и READY!; AES-проверка Exchanger при этом сохранена.
  • Exchanger теперь восстанавливает ключ как Unity. Контрольная пара спецификации 8C70…0F7A → 99ED…B0E1 воспроизводится точно.
  • Probe на COM3 подтвердил RAES == AES-256-ECB-Encrypt(07). Исторический переход в Ready сразу после RAES впоследствии отменён записью от 2026-08-31: актуальный контракт требует точный READY!.
  • Реальный ответ цифровых входов имеет вид INPUT<decimal> без обязательного пробела; parser принимает оба варианта и защищён регрессионными тестами.
  • Безопасная аппаратная группа прошла 8/8: handshake, 91, 140, 150, 160, 309?, 99 и три reconnect-цикла. Ни оплата, ни выдача не запускались.
  • Сборка завершилась без предупреждений и ошибок; модульный набор прошёл 204/204.

2026-08-24 — Первый безопасный прогон реальной платы

  • Windows определила контроллер как Silicon Labs CP210x на COM3; порт 115200 8N1/None открывается стабильно.
  • Первый запуск обнаружил реальный Windows edge case: SerialPort.Read при штатном закрытии бросает OperationCanceledException. Worker теперь безопасно завершает поток; добавлены 6 регрессионных проверок, общий NUnit-набор вырос до 203/203.
  • Первые восемь production-попыток стабильно получили CHAL и RAES, но завершились до Ready: обфусцированный массив ошибочно использовался как эффективный AES-ключ. Причина устранена последующей сверкой с Unity.
  • Добавлен изолированный HardwareProbe: случайный 07, ответ 08 через production AES-256 ECB/NoPadding, классификация RAES без сохранения сырых блоков.
  • Первый probe подтвердил отсутствие AUTH_CNT_OK и несовпадение при ошибочном прямом ключе. После XOR 0x37 повторный probe подтвердил корректный RAES, а Safe прошёл 8/8 без обхода авторизации.
  • Следующий аппаратный шаг: отдельно выполнить Interactive/Payment/Dispense/Cable с явными подтверждениями и физической подготовкой стенда.

2026-08-24 — Завершён Этап 6

  • Release-профиль получил borderless fullscreen, скрытый курсор, восстановление окна после focus/display/resolution change; F10 жёстко ограничен debug-сборкой и состоянием Home.
  • Добавлены Windows watchdog, установка/удаление задачи автозапуска и эксплуатационная инструкция; PowerShell разобран без ошибок, installer проверен через -WhatIf, watchdog — одноразовым процессным запуском.
  • Первоначально реализованы физический SERVICE → PIN-клавиатура → роли Operator/Engineer, пять попыток и 30-секундная блокировка; последующая запись выше заменила environment-PIN единым PIN приложения.
  • SettingsScreen завершён восемью вкладками, полной валидацией, инженерными COM/window настройками, safe reset, подтверждением dirty-exit, отдельным повторным подтверждением restart-only сохранения и итогом без автоматического restart/reconnect.
  • Диагностика показывает версию 0.6.0, тему, безопасное состояние порта/соединения/запаса, последние очищенные предупреждения и тестовую выдачу одного жетона с двойным подтверждением.
  • Добавлены безопасный audit конфигурации, уровни/лимиты журналирования, очистка по сроку и общему объёму; пути, значения, PIN, платёжные данные и сырые протокольные кадры не записываются.
  • Ускоренный soak моделирует 1440 минут, 480 подтверждённых выдач и 10 000 циклов SessionFlow; process-runner прошёл 12/12 пятиисекундных сэмплов с пиком working set 217.73 МБ, CSV дополнительно исправлен и повторно проверен под русской локалью.
  • Runtime QA пройдена на 720×1280 и 1080×1920: роли, Equipment/Diagnostics, field validation, discard/continue, test dispense и двухэтапное restart-only сохранение. Тестовые изменения пользовательской конфигурации восстановлены; game/editor logs чистые.
  • Контрольная сборка завершилась с 0 ошибками и 0 предупреждениями; 197/197 NUnit-тестов прошли, 15 hardware-тестов безопасно пропущены без подключённой платы.
  • Реальный 24-часовой wall-clock soak, проверка собственной экранной клавиатуры на физической панели и поведение дисплея перенесены в аппаратную приёмку Этапа 8 и явно отмечены как внешняя зависимость, а не незавершённая программная задача Этапа 6.

2026-08-21 — Подтверждён профиль AES платы

  • Рабочая реализация владельца подтвердила AES-256, CipherMode.ECB, PaddingMode.None и шифрование одного 16-байтового блока через TransformFinalBlock.
  • Существующий Aes256EcbBlockCipher уже полностью соответствовал этой реализации; ControllerAesMode: "ECB" включён по умолчанию.
  • Конфигурация повышена до версии 6; старые пользовательские настройки с пустым AES-режимом автоматически мигрируют на ECB.
  • Позднейшая сверка с Unity показала, что сохранённый массив нужно декодировать XOR 0x37; после исправления старый RAES-пример и реальная плата совпали.

2026-08-21 — Handshake приведён к фактическому порядку платы

  • На основании предоставленного тогда рабочего метода порядок был уточнён до 07 → CHAL → 08 → RAES. Эта историческая схема заменена 2026-08-31 строгим startup-контрактом banner → 2000 ms → 07/CHAL/08/RAES → READY!.
  • Добавлены состояния ожидания CHAL и доказательства платы; обязательное ожидание AUTH_CNT_OK впоследствии удалено по фактической трассе.
  • Реализованы быстрый CHAL-timeout min(1200, max(250, timeout/4)), общий настраиваемый timeout и отмена ожидания при disconnect.
  • Конфигурация обновлена до версии 5 с HandshakeTimeoutMilliseconds: 5000.
  • Wire-симулятор и интеграционные тесты переведены на новый порядок; набор расширен до 98 тестов, включая закрытие порта при неверном доказательстве платы.

2026-08-21 — Завершён Этап 3

  • Документированный профиль протокола 2.3.5 закрыт: framing, parser, double handshake, платежи, обязательные ASK:*, Вендист, SERVICE, INPUT и legacy-хоппер отделены от транспорта и Godot Node.
  • Добавлен PeripheryControllerSimulator, который проходит настоящий handshake и воспроизводит card success, malformed-кадр, бинарный датчик, полную и частичную выдачу без COM-порта.
  • Протокольный адаптер игнорирует бизнес-кадры до криптографически подтверждённого RAES, подавляет повторный DISPENSE, не выполняет автоматический retry и блокирует legacy-хоппер после аппаратной ошибки.
  • Добавлены типизированные команды status-запросов 140, 150, 160, 99; неизвестные форматы ответов остаются безопасно неподдерживаемыми.
  • Внешние неопределённости AES, силового канала и физической распиновки вынесены в аппаратную проверку следующих этапов и не подменены предполагаемыми командами.
  • Набор расширен до 91 теста; проверены все результаты card-сессии, несовпадение суммы, pre-auth-фильтрация, service/error, malformed recovery, границы параметров, at-most-once и partial hopper failure.

2026-08-21 — Фиктивный числовой остаток полностью удалён

  • Аппаратная граница использует только MachineStockLevel: Enough, Low или технический Unknown.
  • Числовой остаток, MachineStockSnapshot, ExactCount, Empty, Pricing.InitialStock и пороги удалены из real/mock-контроллеров, сессии, UI и конфигурации.
  • Добавлен parser ответа INPUT<bitmask>/INPUT <bitmask>, polling командой 91 и конфигурация номера входа/полярности датчика.
  • UI и mock работают только с «МНОГО»/«МАЛО»; успешная выдача не изменяет уровень без нового сигнала датчика.
  • Unknown блокирует начало оплаты, Low разрешает работу без ложной проверки точного остатка.
  • Тестовый набор расширен до 77 тестов, включая polling датчика и сброс уровня в Unknown при disconnect.

2026-08-21 — Добавлен предоставленный ключ контроллера

  • 32-байтовый AesEncryptedKey добавлен в PeripheryControllerSecurity и документацию протокола.
  • Переменная EXCHANGER_CONTROLLER_AES_KEY сохранена как необязательное переопределение для другой ревизии платы.
  • Позднее выяснилось, что массив является обфусцированным: после XOR 0x37 AES-256 ECB/NoPadding воспроизводит пример RAES и обмен с реальной платой.

2026-08-21 — Начата интеграция периферийного контроллера 2.3.5

  • Добавлен System.IO.Ports 8.0.0 и реальный Windows-транспорт 115200 8N1 без flow control.
  • COM-чтение/запись выполняются одним worker-потоком; события передаются в Godot через очереди и главный _Process.
  • Реализован ASCII-фреймер CR/LF, timeout 100 мс, лимит кадра и безопасное восстановление после мусора/переполнения.
  • Добавлены parser/formatter команд READY, handshake, MONEY:*, ASK:*, SERVICE, Вендиста и legacy-хоппера.
  • Реализован double-handshake state machine и AES-256 ECB cipher; на этом шаге активация ещё ожидала подтверждения режима, которое владелец предоставил позднее.
  • PeripheryMachineController управляет smart-монетником, купюроприёмником, Вендистом, keep-alive и обязательными подтверждениями платежей.
  • Приём оплаты блокируется, пока не настроен безопасный production-способ выдачи; автоматический retry выдачи отсутствует.
  • Добавлен периодический reconnect COM-порта и повторная авторизация после подключения.
  • Тестовый набор расширен до 72 тестов, включая framing, команды, handshake, AES-вектор, проверку встроенного ключа, платежи, keep-alive, disconnect во время выдачи и блокировку небезопасной выдачи.

2026-08-21 — Добавлен безопасный контекст и журнал операций

  • Каждая cash/card-сессия получает уникальный локальный OperationId; начало и единственный итог публикуются типизированными снимками.
  • Добавлены SessionErrorCode, безопасные пользовательские тексты и рекомендации ReturnHome/TryAnotherPaymentMethod/ContactSupport; сырая ошибка оборудования не попадает в UI.
  • Отдельный JSONL-журнал user://logs/operations.jsonl хранит только безопасные поля и при старте закрывает незавершённые записи как UnknownAfterRestart, не повторяя оплату или выдачу.
  • IMachineController и MockMachineController поддерживают событие потери связи; потеря во время выдачи приводит к DispenseStatusUnknown и запрещает предполагать результат.
  • Debug-панель карточного сценария позволяет вручную воспроизвести успех, отказ, полную/частичную ошибку выдачи и потерю связи.
  • Правила восстановления и ручная приёмка зафиксированы в docs/operations/RECOVERY_AND_MOCK_QA.md.
  • Набор расширен до 45 тестов; проверены уникальность идентификаторов, единственный финальный исход, typed errors, очистка подписок и восстановление JSONL.

2026-08-21 — Автоматизировано тестирование mock-среза

  • Подключены NUnit 4.6.1, NUnit3TestAdapter 6.2.0 и Microsoft.NET.Test.Sdk 18.9.0 в отдельном Exchanger.Tests.
  • Бизнес-логика вынесена из Godot-узла в чистый SessionFlow; SessionController остался тонким адаптером _Process и журналирования.
  • Добавлен IMonotonicClock, благодаря которому таймауты 45/120 секунд проверяются мгновенно управляемыми часами.
  • PricingPolicy отделяет проверку суммы и расчёт базовых/бонусных жетонов от UI и Godot.
  • Набор из 37 тестов покрывает конфигурацию, тарифы, cash/card success, decline, timeout, неподтверждённое состояние запаса, duplicate/late events, частичную выдачу, сброс данных и утечки подписок.
  • Автоматически проверяется наличие восьми сцен и четырёх ресурсов тем; семь пользовательских экранов отдельно прошли runtime smoke-загрузку через godot-ai.
  • В тот момент mock временно проверял выбранный пакет против числового запаса; позже это поведение удалено после уточнения, что датчик сообщает только «много/мало».
  • MockMachineController получил настраиваемую задержку и детерминированные сценарии полной/частичной ошибки выдачи; полный отказ runtime-проверен до корректного ErrorScreen.
  • Итоговые dotnet test, запуск проекта и Godot editor/game logs проходят без ошибок и предупреждений.

2026-08-21 — Завершён Этап 2

  • Добавлена типизированная аппаратная граница IMachineController и MockMachineController для баланса, запаса, карточного результата и подтверждения выдачи без предположений о wire-протоколе.
  • Реализован SessionController с явными cash/card состояниями, монотонными таймаутами, сбросом временных данных и защитой от повторных команд.
  • Добавлены CardCustomAmountScreen, CardTerminalScreen, SuccessScreen и ErrorScreen; все экраны подключены через ScreenRegistry.
  • Наличная ветка получила debug-ввод купюр и явное подтверждение выдачи; карточная ветка — debug-результаты терминала.
  • Реализован автономный AdvertisementPlayer: локальный плейлист, постер/текстовый fallback, беззвучный режим по умолчанию и остановка декодирования вне HomeScreen.
  • Runtime пройдены cash success, card success, card decline и custom amount; проверены выдача только после подтверждения, отображение запаса и возвраты на Home.
  • Визуальная QA 720×1280 при логическом viewport 1080×1920 выявила и устранила неравномерную цифровую клавиатуру; финальная сетка состоит из трёх равных колонок.
  • Контрольная C#-сборка и Godot runtime-логи чистые; новые NuGet-зависимости не потребовались.

2026-08-20 — Первый пакет Этапа 2

  • Логический viewport переведён на 1080×1920, debug-window — на 720×1280; добавлена миграция пользовательской конфигурации до версии 2.
  • Добавлены Input Actions kiosk_back и debug_cycle_theme.
  • Реализованы Autoload ScreenManager, централизованный ScreenRegistry, общий ScreenHost и UI fade-переходы.
  • После runtime-теста добавлен TransitionInputBlocker, устраняющий случайный выбор элемента под координатой отпущенного тача на новом экране.
  • Удалён ландшафтный StartScreen; созданы HomeScreen, CashPaymentScreen и CardAmountScreen.
  • Добавлены конфигурируемые тарифы, бонусы, карточные пресеты, брендинг, настройки промо и TokenStockStatus.
  • Космическая тема переведена с фиолетового прототипа на тёмно-синюю/салатовую палитру референса.
  • Визуально и через runtime UI проверены Home → Cash → Home → CardAmount → Home на 720×1280 при логическом viewport 1080×1920.
  • Разрешение на необходимые NuGet-пакеты получено; в текущем пакете новые зависимости не потребовались.

2026-08-20 — Завершён Этап 0

  • Инициализирован локальный Git-репозиторий без назначения remote и создания первого коммита.
  • .agents/AGENTS.md признан проектным файлом и исключён из списка игнорирования.
  • Создан docs/architecture/decisions.md с базовыми архитектурными решениями.
  • До появления целевого оборудования принята универсальная, конфигурируемая стратегия без привязки к конкретному ПК, GPU, DPI или тач-контроллеру.
  • Реализация wire-протокола и COM перенесена в Этапы 3–4; входом станет документация и список команд, которые владелец позднее добавит в references/.
  • Зафиксировано, что все аппаратные данные поступают ПК от контроллера и преобразуются внутри приложения в типизированные события.

2026-08-20 — Инициализация дорожной карты

  • Изучено исходное ТЗ и текущее состояние проекта.
  • Исторически был зафиксирован Compatibility renderer вместо противоречащего ему Forward+; текущий режим — Mobile/Vulkan для Native Video.
  • Сформирована архитектура тем, навигации, сессии и аппаратного слоя.
  • Определены этапы разработки, критерии приёмки, риски и вопросы к владельцу продукта.

2026-08-20 — Завершён Этап 1

  • Исторически были настроены viewport 1920×1080, Compatibility, production fullscreen и debug-window профиль; текущий рендерер — Mobile/Vulkan.
  • Созданы App, StartScreen, TouchInputGuard, AppSettings и журнал с ротацией.
  • Реализованы VendingThemeDefinition, Autoload ThemeManager, базовая и космическая темы.
  • Добавлен анимированный shader-фон, космические частицы и статический fallback для облегчённого режима.
  • Созданы IHardwarePort, рабочая mock-реализация и безопасные Windows/Android каркасы.
  • Проверены C#-сборка без предупреждений, main scene, runtime-логи, смена тем, кнопка «Начать», mock PING и создание пользовательской конфигурации.
  • Визуально проверены 1280×720, 1024×768 и borderless fullscreen 2560×1440.
  • Подтверждено, что для реального Windows SerialPort потребуется предварительно согласованный NuGet-пакет System.IO.Ports.

2026-08-20 — Исправлена корневая документация

  • README.md полностью заменён документацией Exchanger на русском языке.
  • Добавлены описание продукта, текущее состояние, запуск, конфигурация, журналы, архитектура тем и аппаратного слоя.
  • Удалён случайно установленный корневой LICENSE GodotX Toast.
  • Лицензии сторонних компонентов в addons/ и godot-ai-LICENSE.txt сохранены.
  • Зафиксировано отсутствие объявленной собственной лицензии Exchanger.

2026-08-20 — Учтены визуальные референсы аппарата

  • references/ добавлен в .gitignore как локальный источник концептов, не предназначенный для production.
  • Проанализированы семь макетов UI 720×1280, рендер корпуса и видео 1080×1920/50 FPS длительностью 32,16 секунды.
  • Целевая ориентация изменена с ландшафтного технического прототипа на портретную 9:16.
  • Зафиксированы домашний экран, наличная ветка, карточные суммы/клавиатура/терминал, отказ оплаты, запас жетонов, бонусы, промо-блок и поддержка.
  • План Этапов 2, 3, 5 и 7 переработан с учётом реального корпуса и пользовательского потока.
  • Подробный разбор сохранён в docs/REFERENCE_ANALYSIS.md.

2026-08-20 — Фон приведён к референсам

  • AnimatedBackground заменён на StaticBackground.
  • Из активной сцены и VendingThemeDefinition удалены фоновые shader-материалы и частицы.
  • Удалены ставшие ненужными shader/particle-ресурсы обеих тем.
  • Сохранена поддержка статической текстуры фона и отдельной PackedScene с анимированными спрайтами.
  • Промо-блок отделён в архитектуре от фонового слоя приложения.

2026-08-21 — Завершена программная часть Этапа 4

  • В IMachineController добавлено безопасное состояние соединения Disconnected/Authenticating/Ready/Faulted, одинаковое для mock и реального протокольного адаптера.
  • Потеря связи переводит бинарный запас в Unknown, блокируя начало новой оплаты без вымышленного остатка.
  • Добавлен атомарный ConnectionStatusIndicator: в production он не занимает внимание при Ready, но показывает авторизацию, обрыв и ошибку; debug также показывает штатное подключение.
  • Интеграционный тест выполняет 200 reconnect-циклов с обрывом во время выдачи и подтверждает отсутствие автоматического повтора и позднего завершения операции.
  • Полный набор расширен до 100 тестов; сборка без предупреждений, Godot 4.7.1 runtime и журналы чистые, индикатор визуально проверен на 720×1280.
  • Физический длительный прогон и отключение USB-кабеля остаются внешней приёмкой до появления контроллера.

2026-08-21 — Подготовлена аппаратная приёмка платы

  • Добавлен отдельный проект Exchanger.HardwareTests из 15 NUnit-проверок production-пути SerialPortHardware → ControllerAuthenticator → PeripheryMachineController.
  • Безопасная группа проверяет COM 115200 8N1, production AES-handshake, INPUT, status-команды, закрытие и повторную авторизацию.
  • Ручные группы покрывают SERVICE, монетник, купюроприёмник и оба бинарных состояния датчика; отсутствующую периферию можно пропустить независимо.
  • Реальная карточная операция, физическая выдача и отключение USB требуют разных явных подтверждений и не могут стартовать из обычного dotnet test.
  • PowerShell-runner создаёт локальный TRX-отчёт в artifacts/board-tests/; сырые кадры, AES-данные и секреты в отчёт не попадают.
  • Полная инструкция запуска и порядок приёмки сохранены в docs/hardware/BOARD_ACCEPTANCE_TESTS.md.

2026-08-21 — Разработка тем полностью вынесена из Exchanger

  • Каталог themes/, встроенные .tres-темы и debug-переключение удалены из основного проекта.
  • Exchanger содержит только программную fallback-тему и runtime-контракт одного внешнего DLC; списка тем и горячего переключения больше нет.
  • Конфигурация версии 7 читает Themes.ExternalDlcPath из пользовательского appsettings.json; по умолчанию используется user://theme_dlc/active_theme.pck.
  • Реализован формат ExchangerThemeDlc v1: .pck до 512 МиБ, обязательный manifest и Theme, необязательные статическая текстура и PackedScene анимированных спрайтов.
  • Пакет монтируется с replace_files=false; пути ограничены res://exchanger_theme_dlc/, повреждённый DLC безопасно оставляет fallback.
  • В соседнем ../ExchangerThemeDlcBuilder создан независимый Godot 4.7-проект с manifest, примером темы, статическим фоном и PowerShell-сборщиком/установщиком.
  • Реальный .pck успешно собран и загружен Exchanger через production loader; fallback отдельно визуально проверен в Godot на 720×1280.
  • Набор расширен до 105 автоматических тестов плюс 15 отключённых по умолчанию аппаратных проверок; solution собирается без предупреждений.

2026-08-21 — Создана первая внешняя тема space_pixel

  • В независимом ../ExchangerThemeDlcBuilder обработаны предоставленные 46 PNG, статический фон 1080×1920, кириллический pixel-font и восемь покадровых последовательностей.
  • Оригиналы сохранены в themes_src/space_pixel/; runtime-копии получили стабильные ASCII-пути и не зависят от основного проекта.
  • Реализованы ui_theme.tres, статический фон и пассивная decoration-сцена мерцающих звёзд и пролетающих НЛО без скриптов, шейдеров, частиц и видео.
  • В manifest v1 добавлен обратно совместимый флаг UseNearestTextureFilter; space_pixel включает nearest, остальные DLC сохраняют стандартную фильтрацию.
  • Экспорт текстовых .tres/.tscn в бинарный формат отключён, поэтому переносимый PCK загружается по стабильным путям без cross-project UID-предупреждений.
  • build/active_theme.pck собран и независимо проверен production-загрузчиком Exchanger: тема активируется, приложение завершает smoke-run без ошибок и предупреждений.
  • Контекстные анимации оплаты подготовлены в builder, но остаются не подключёнными до появления именованных screen-specific слотов в следующей версии DLC-контракта.
  • В локальный игнорируемый каталог themes_src/space_pixel/references/ скопированы восемь JPG и видео интерфейса; reference-медиа не экспортируются в PCK.
  • После просмотра всех макетов и 64 кадров видео тема получила семантические вариации ScreenTitle, PanelTitle, PanelText, SupportText, PrimaryButton, CompactButton, BackButton и WhitePanel.
  • Preview перестроен по наличному экрану референса в 720×1280; runtime-декорация при этом сохраняет координаты host-приложения 1080×1920 и масштабируется только внутри preview.
  • Для полного reference-look основному Exchanger позднее потребуется назначить semantic theme_type_variation, вынести общий footer и добавить screen-specific слоты анимаций; точный список узлов зафиксирован в builder docs/SPACE_PIXEL.md.
  • Builder-preview расширен до переключаемой галереи всех семи пользовательских экранов темы: Home, Cash, CardAmount, CardCustom, CardTerminal, Success и Error.
  • В builder-preview добавлен live reload: сохранённые изменения Theme, изображений и preview-сцен автоматически появляются в запущенной галерее без ручного перезапуска и без сброса выбранной вкладки; preview-инструмент не экспортируется в DLC.
  • Layout всех семи preview-сцен приведён к общему правилу: после зелёной шапки сохраняется заметный белый отступ, кнопки не прилипают к заголовкам, а изображения и экранные анимации центрируются относительно внутренней области WhitePanel.

2026-08-24 — Начата реализация сервисных настроек

  • Конфигурация повышена до версии 8 и получила AudioSettings: master volume, master mute и отдельную громкость промо-ролика.
  • Добавлен чистый C# SettingsService с изолированной рабочей копией и неизменяемым активным снимком, атомарной записью через временный файл, .bak предыдущей корректной версии и восстановлением после повреждения JSON.
  • App загружает настройки через новый сервис и применяет master volume к Godot audio bus; AdvertisementPlayer отдельно применяет громкость промо-ролика, сохраняя существующий ad mute.
  • Миграция реального пользовательского файла 7 → 8 проверена в runtime: создана резервная версия 7, временный файл удалён, домашний экран и журналы Godot чистые.
  • NUnit-набор расширен до 119 тестов: добавлены аудиограницы, working-copy isolation, round-trip сохранения, backup, восстановление из backup/defaults, защита от конфигурации будущей версии, безопасный запуск при невозможности записи и smoke-проверка новой сцены.
  • Добавлен атомарный SettingsScreen для звука: рабочая копия, live-preview, сохранение через SettingsService, отмена с восстановлением активного снимка и безопасный синусоидальный тестовый сигнал.
  • Master и промо разведены по отдельным Godot audio buses; bus промо-ролика отправляется в Master и меняется независимо без двойного пересчёта громкости в проигрывателе.
  • Вход по F10 возможен только из Home и только при OS.IsDebugBuild(); production-вход остаётся закрытым до реализации отдельной авторизации сервисного персонала.
  • Экран и действия сохранения/отмены проверены в runtime на 1080×1920 и 720×1280; повторный тестовый сигнал и оба аудиоканала не оставляют ошибок в Godot game/editor logs.
  • SettingsScreen расширен вкладкой «Тарифы и бонусы»: цена жетона, динамические строки порог → бонус, добавление, удаление, сортировка перед сохранением и preview типовых сумм через production PricingPolicy.
  • В интерфейсе явно показано правило расчёта: из подходящих строк берётся один наибольший бонус, строки не суммируются.
  • Добавлен чистый PricingSettingsValidator: блокирует нулевую/чрезмерную цену, цену выше максимальной суммы, пороги вне диапазона, отрицательные/чрезмерные бонусы и дубли порогов; ошибки цены и каждой строки показываются рядом с полем.
  • Сохранённые тарифы безопасно применяются только из Home: заново настраиваются SessionController, наличный, карточный и ручной экраны, после чего восстанавливаются stock/connection state; активную операцию редактор открыть не может.
  • NUnit-набор расширен до 126 тестов; тарифный UI проверен в runtime на 720×1280 и 1080×1920, включая add/remove, duplicate validation, disabled Save, preview, Cancel и сохранение без ошибок Godot.
  • Конфигурация повышена до версии 9 и получила отдельный блок Payments; runtime-миграция реального файла 8 → 9 создала корректную резервную версию 8 без оставшегося временного файла.
  • В SettingsScreen добавлена вкладка «Оплата»: независимое включение наличной и карты, максимальная карточная сумма, шаг ручного ввода, динамический редактор пресетов и три пользовательских таймаута с пояснениями безопасных диапазонов.
  • Карточные пресеты сортируются, проверяются на диапазон, кратность шагу и дубли. Пустой список разрешён, поскольку карточная ветка сохраняет действие «Другая сумма».
  • PaymentSettingsValidator запрещает отключение обоих способов оплаты и проверяет таймауты; PricingSettingsValidator дополнен проверкой максимума, шага и каждой строки пресета.
  • SessionFlow отклоняет отключённый способ до начала операции и обращения к контроллеру, а HomeScreen сразу блокирует соответствующую кнопку вместе с существующей блокировкой по запасу.
  • Настройки безопасно применяются только из Home; runtime-проверка временно отключила наличную оплату, подтвердила блокировку кнопки и восстановила исходную конфигурацию.
  • NUnit-набор расширен до 137 тестов; 15 тестов физической платы по-прежнему безопасно пропускаются без явного флага.
  • Экран «Оплата», add/remove, ошибки способов оплаты, диапазонов и disabled Save проверены в Godot 4.7.1 на 720×1280 и 1080×1920; финальные game/editor logs чистые.
  • SettingsScreen получил стартовую вкладку «Общие» с четырёхкнопочной навигацией, однострочными полями названия, подзаголовка и контакта техподдержки, ограничениями длины и отдельным preview домашнего экрана.
  • Добавлен чистый BrandingSettingsValidator: обязательность, максимальная длина и запрет управляющих символов проверяются до записи; runtime-нормализация сворачивает пробелы и безопасно ограничивает текст из внешнего JSON.
  • Сохранение общих данных проверено сквозным runtime-сценарием до HomeScreen; тестовые значения после проверки заменены исходными, пользовательская конфигурация версии 9 не оставила временного файла.
  • NUnit-набор расширен до 146 тестов; вкладка «Общие», пустое поле, disabled Save, живой preview и сохранение проверены в Godot 4.7.1 на 720×1280 и 1080×1920, журналы чистые.
  • SettingsScreen получил пятую вкладку «Промо»: включение блока, отдельный mute промо-роликов, fallback-текст, необязательный poster, динамический OGV-плейлист до 12 строк и сводный preview состояния.
  • Новый AdvertisementSettingsValidator принимает только res://, user:// или абсолютные локальные пути, запрещает traversal/сетевые URL, проверяет форматы PNG/JPG/JPEG/WEBP и OGV, существование файлов, дубли и длины до сохранения.
  • Отключённое промо намеренно позволяет сохранить временно недоступные пути: AdvertisementPlayer в этом режиме не загружает poster, не создаёт видеопоток и показывает «ПРОМО ОТКЛЮЧЕНО».
  • AdvertisementPlayer дополнен загрузкой внешнего raster-постера через Image и локального OGV через VideoStreamTheora; диагностическое предупреждение больше не раскрывает полный путь файла.
  • Runtime-проверка покрыла пустой fallback, отсутствующий poster, пустой/отсутствующий промо-ролик, add/remove, disabled Save и безопасное сохранение отключённого блока; после теста исходные настройки промо восстановлены.
  • NUnit-набор расширен до 157 тестов; вкладка «Промо» визуально проверена в Godot 4.7.1 на 720×1280 и 1080×1920, game/editor logs чистые.
  • SettingsScreen получил шестую вкладку «Тема»: включение единственного внешнего DLC, путь к .pck, статус доступности пакета и облегчённый режим эффектов.
  • Новый ThemeSettingsValidator принимает только user:// либо абсолютный локальный путь к .pck, запрещает traversal и управляющие символы; отсутствие будущего пакета не мешает сохранить корректный путь и безопасно оставить fallback-тему.
  • В UI и runtime зафиксирована граница применения: ReducedEffects переключается сразу и откатывается кнопкой «Отменить», а включение DLC и путь применяются только при следующем запуске без самопроизвольной перезагрузки темы.
  • NUnit-набор расширен до 166 тестов; неверный путь, отключённый DLC, live-preview/откат и внешний вид вкладки проверены в Godot 4.7.1 на 720×1280 и 1080×1920, пользовательские настройки восстановлены, game/editor logs чистые.

2026-08-24 — Начат Этап 7 и синхронизирован host-контракт тем

  • Все девять runtime-экранов получили семантические theme_type_variation; встроенная тема регистрирует их как безопасные наследники базовых Godot-классов, поэтому fallback не изменился.
  • Динамически создаваемые строки настроек также используют PanelText и CompactButton, при этом CheckButton и OptionButton сохраняют собственные базовые типы.
  • ScreenManager отключает processing скрытого экрана и включает его только перед показом нового; runtime-переход Home → Settings подтвердил один активный экран и восемь отключённых.
  • Зафиксирован debug-baseline на 720×1280 и 1080×1920: стабильные 60 FPS, до 37 draw calls и менее 1,4 мс process time; условия и ограничения записаны в docs/operations/PERFORMANCE_BASELINE.md.
  • Визуально проверены fallback Home 720×1280 и Settings 1080×1920 после внедрения семантических типов: элементы читаемы, панели и кнопки не пропали, наложений не обнаружено.
  • Solution собирается без предупреждений; 223 unit/integration-теста проходят, 17 физических hardware-тестов остаются безопасно пропущенными без явных разрешающих флагов.
  • Decoration внешнего DLC получила жёсткий runtime-бюджет узлов/спрайтов/кадров и частоты; текущая space_pixel занимает лишь 5 узлов, 4 CanvasItem, 1 AnimatedSprite2D и 2 кадра при 1 FPS.
  • Runtime-аудит всех девяти экранов устранил неравные колонки цифровых клавиатур и подтвердил отсутствие мелких пользовательских кнопок и логики, зависящей от hover.
  • Соседний Theme Builder синхронизирован с девятью экранами и восемью разделами Settings, расширенными variations, hot reload и export-аудитом; PCK установлен в стандартный user://.
  • space_pixel загружен production loader без UID-предупреждений; визуально проверены Home, ServiceAccess и Settings/Equipment на 720×1280, а full/reduced профиль сохраняет 60 FPS.
  • После дополнительного host-QA исправлен контраст CheckButton; финальный PCK (167280 байт, 30 entries) установлен в user://, все 8 Settings-разделов и SERVICE проходят runtime smoke.

2026-08-24 — Начат Этап 9: воспроизводимый Windows export

  • Добавлен preset Windows x64 и tools/export-windows.ps1, который собирает release из очищенной staging-копии, не меняя рабочий проект и не включая editor-autoload.
  • Из PCK исключены references/, тесты, docs, tools, artifacts, agent-файлы и все addons; проверенный каталог не содержит test DLL или addon-файлов.
  • Первая self-contained сборка 0.6.0 успешно создана: 189 файлов, 180,8 МиБ, внутри присутствуют Exchanger.dll, GodotSharp.dll и System.IO.Ports.dll.
  • Экспортированный Exchanger.exe прошёл headless smoke-run с exit code 0; порядок повторной сборки и pre-release проверки записан в docs/operations/WINDOWS_EXPORT.md.
  • Финальный export после semantic/touch-правок повторно собран в artifacts/windows/Exchanger-0.6.0-20260824-163811: 189 файлов, 180,8 МиБ, exit code 0.

2026-09-07 — Составные действия интерактивного питомца переданы теме

  • Добавлен обратно совместимый optional composite contract: тема владеет последовательностью клипов и визуальных преобразований, а Exchanger передаёт scoped runtime для проверенного движения anchor, политик ввода/границ/опор и посадки на доступную поверхность.
  • space-pixel реализует interact: условный поворот, come_closer с scale ×2, повтор action, вычисляемый проход falling через низ/верх экрана без промежуточных опор, landing на самой высокой опоре и возврат в idle; reduced-effects получает сокращённую ветку.
  • Runtime автоматически инвалидируется при смене экрана/темы, drag и конфликтующем действии; темы без нового API сохраняют прежний одиночный interact fallback.
  • NUnit и полный InteractivePetBehaviorSmoke проверяют выбор верхней опоры, PCK-контракт, scale ×2, вертикальный wrap, точный порядок клипов и сохранение прежних drag/drop, маршрутов, смены экранов и четырёх циклов празднования.

2026-09-07 — Host синхронизирован с актуальными документами и PCK темы

  • Подтверждён актуальный контракт: 481 immutable binding + 17 обязательных supplemental ID; Sync-HostBindingContract.ps1 теперь явно исключает supplemental-набор из JSON-генерации, а Test-PackagedBindingContract.ps1 защищает все 17 ID от случайного попадания в каталог.
  • Зафиксирован trusted runtime без устаревшего decoration budget. space-pixel публикует 22 анимации/414 кадров и новые имена walking start/stop через semantic mapping; app-код не привязывается к конкретным именам клипов темы.
  • Свежий space-pixel.pck 1658512 байт, SHA-256 76518563591159EC4B8087911D2BB29924BC29C12AAA4FF1544D97ED01648E91, прошёл полный InteractivePetBehaviorSmoke по явному пути на mock-оборудовании.
  • PaymentRewardFormattingSmoke больше не сохраняет пользовательские Settings и сравнивает наличный/карточный формат на фактическом пресете и бонусном правиле вместо жёсткого ожидания нулевого бонуса.

2026-09-08 — Приземление и простой питомца включены в обычное поведение

  • После падения при перетаскивании или смене экрана host проигрывает цепочку falling → landing → idle; landing выполняется один полный цикл, а idle остаётся состоянием ожидания до следующего автономного действия.
  • Старые темы без анимации landing продолжают сразу переходить в idle.
  • Запрошенные во время падения четыре цикла dancing начинаются только после завершения приземления.
  • Runtime-smoke с актуальным space-pixel.pck и mock-оборудованием подтвердил touch drag, смену экранов, falling → landing → idle, отложенный танец и composite action. Debug и Release прошли по 488/488 NUnit-тестов; 18 аппаратных тестов в каждой конфигурации пропущены.

2026-09-08 — Вероятностный простой после ходьбы питомца

  • После завершения маршрута host с вероятностью 35% выбирает idle на 3–5 секунд, а в остальных случаях — sitting на 1,5–3,5 секунды. Темы без sitting безопасно используют idle.
  • InteractivePetBehaviorSmoke допускает оба корректных результата маршрута и продолжает проверять falling → landing → idle. Unit-тесты фиксируют границу вероятности.
  • Godot 4.7.1 начал штатно добавлять unique_id в заголовки узлов .tscn; хрупкая проверка фиксированного футера Settings адаптирована к дополнительным атрибутам без изменения сцены.
  • Debug и Release прошли по 496/496 NUnit-тестов; 18 аппаратных тестов в каждой конфигурации пропущены. Runtime-smoke с новым space-pixel.pck на mock-оборудовании завершился успешно.

2026-09-09 — Действие питомца во время подъёма и спуска

  • На каждом ненулевом ребре подъёма или спуска host выбирает случайную точку между 20% и 80% пути, останавливает перемещение и выполняет climbing_action_start → climbing_action → climbing_action_stop; средний клип проигрывается один полный цикл.
  • После stop возобновляется исходная анимация направления и продолжается тот же маршрут. Для старых тем возможность включается только при наличии всех трёх действий, поэтому частичная последовательность не возникает.
  • Reduced-effects теперь действительно исключает автономный подъём сразу после landing, но не блокирует явно запрошенные маршруты и их climbing-action.
  • Debug и Release прошли по 529/529 NUnit-тестов. Полный InteractivePetBehaviorSmoke с явным актуальным space-pixel.pck и mock-оборудованием подтвердил порядок клипов, неподвижность anchor во время sequence, продолжение маршрута, смены экранов и составное взаимодействие.