Files
agents/docs/agent/COMPLETED_TASKS.md
T
2026-10-11 15:02:38 +07:00

89 KiB
Raw Blame History

Завершённые задачи

Архив задач, переведённых в статус DONE.

ED-029 — Автономная площадка и панель организатора

  • Статус: DONE — закрыта 2026-10-11 по явному указанию пользователя; код и автоматические проверки завершены, аппаратная приёмка этапа 15 не выполнена.
  • Репозитории: корневой, evening_detective_server, evening_detective_frontend.
  • Цель: реализовать принятый пользователем план из 15 последовательных этапов: автономное окружение; локальные организаторы; прежние адреса через Cudy; пакет игры; экспорт со статусом офлайн-режима; локальный импорт; интерфейс передачи; полный билетный и игровой flow; пакет результатов; возврат онлайн; интерфейс возврата; устройства; панель состояния; сбор данных Cudy; Wi-Fi и испытание площадки.
  • Критерии: проверяемый результат каждого этапа; цикл «онлайн → ноутбук → проведение → онлайн» через интерфейс с сохранением QR, билетов и связей; повторные импорты без дублей; мониторинг устройств и Wi-Fi; полный прогон без WAN, 100 клиентов и проверка покрытия.
  • Ограничения: только тестовые игры до завершения возврата; существующие правила игры и формат offline сохраняются; без несогласованных новых зависимостей и отклонений от стандартов; без персональных списков игроков, CPU/памяти Cudy и автоматического переключения серверов; настройки роутера менять только по явному запросу пользователя; секреты и пакеты вне Git; commit/push выполняет пользователь.
  • Проверка: Go-стандарт и go test ./...; PostgreSQL-интеграционные тесты для атомарности; frontend-стандарт, type-check, lint, lint:css и build; штатная генерация protobuf; ручные контрольные точки после этапов 3, 8, 11, 15.
  • Прогресс: этапы 1–2 реализованы и проверены в локальном Docker-окружении. Добавлены отдельный Compose, подготовка образов заранее, постоянные тома, миграции перед стартом, healthchecks и прокси. Явный APP_MODE=venue отключает SMTP, фоновые письма и внешний webhook; почтовые операции аккаунта возвращают безопасную ошибку до изменения данных. Локальные аккаунты создаются штатным dev-user.sql, роли и правила автоматического подтверждения команд сохраняются. Этап 3 принят пользователем после проверки без WAN; формат и код пакета игры (этап 4) реализованы и проверены. Экспорт со статусом офлайн-режима (этап 5) и локальный импорт (этап 6) реализованы и проверены. Этап 7 реализован и проверен; этап 8 проверен на изолированном стенде и принят пользователем на аппаратной контрольной точке; этапы 9–13 реализованы и проверены; этап 14 реализован и проверен на настоящем TR3000; код и автоматические проверки этапа 15 завершены, ожидается аппаратная приёмка Wi-Fi.
  • Дополнение по запросу пользователя: структура venue приведена к prod (backend/, frontend/dist/, migrations/, Dockerfile.backend, docker-compose.yml, deploy.sh); имена основных Compose-сервисов совпадают. Общая генерация make build / make prod-build готовит артефакты обеих папок; frontend собирается отдельно с настройками своего окружения. Текущие файлы prod при этой работе не изменяются; сборка проверяется с production-выводом в /tmp.
  • Упрощение запуска: отдельные init.sh и prepare.sh удалены, всё собрано в deploy.sh. Единственная команда запуска — make venue: создаёт .env только при его отсутствии, при необходимости готовит зависимости/образы и автоматически создаёт организатора. Повторный готовый запуск не собирает и не скачивает образы. make venue-update явно пересобирает окружение с интернетом. Аккаунт организатора синхронизируется со значениями .env при каждом запуске. Первый запуск, повторный запуск без интернета и обновление проверяются python3 tests/venue/deploy.py с подставными командами; реальный готовый запуск проверен через Docker и API-smoke.
  • Настройки площадки: по запросу пользователя необязательный venue/.env.example удалён; настройки создаются автоматически через make venue. Убраны ссылка и проверка примера; python3 tests/venue/deploy.py прошёл. Существующая .env не изменялась.
  • Группировка проверок: deploy.py и smoke.py собраны в tests/venue/ с поясняющим README, пути к площадке и команды в документации обновлены; для обычного запуска эти файлы не нужны. python3 tests/venue/deploy.py и python3 tests/venue/smoke.py --verify прошли; diff проверен.
  • Уточнение названия: автономная площадка называется venue, чтобы не смешивать её с локальной разработкой. Папка, Make-команды, образ, Docker-проект, режим APP_MODE=venue и настройки VENUE_* переименованы; существующая .env перенесена с сохранением секретов и учётной записи. Служебные имена прежних томов сохранены для сохранности данных; тома подключаются как внешние и создаются автоматически при первом запуске. Go-тесты, тест запуска и API-smoke после переименования прошли; prod не изменён. Ручное ревью diff по стандарту Go выполнено.
  • Проверка дополнения завершена: make build PROD_DIR=/tmp/evening-artifacts-check прошёл, backend и миграции в обоих наборах совпадают; type-check и две frontend-сборки прошли. Проверены Compose config, shell-синтаксис и diff. Local собран и запущен через новый deploy.sh; API-smoke и Firefox подтвердили вход, тестовую игру, подтверждённую команду и сохранность материала. Контрольные суммы файлов prod до и после работы совпадают. Проверка production-запуска не выполнялась; существующие файлы и конфигурация prod не изменены.
  • Результат проверки этапов 1–2: ручное ревью diff по Go- и frontend-стандартам выполнено, отклонений нет; gofmt, go test ./..., frontend type-check, lint, lint:css, сборка и полный sh venue/prepare.sh прошли. Compose config и shell-синтаксис проверены. Smoke через API создал тестовую игру, подтверждённую команду и материал; после down / up --no-build --pull never всё сохранилось. Firefox без окна подтвердил реальный вход, экран игры и команду после перезапуска. Приложение не имеет внешнего маршрута сети. Повторная инициализация не перезаписывает секреты. Инструкции запуска и повторяемый API-smoke находятся в корневом README и tests/venue/smoke.py. Проверка телефонов при отключённом WAN ещё не выполнена.
  • Внешние данные: запрошены модель/IP Cudy, постоянный IP ноутбука, доступ к проверенной прошивке и возможность DNS-проверки для сертификатов. Проверки телефонов, 100 клиентов и покрытия требуют оборудования.
  • Привязка ноутбука на Cudy по явному запросу «закрепи сам»: перед изменением подтверждены текущие IP 192.168.10.155 и MAC BC:D2:2C:29:4A:96. Через штатную форму IP/MAC Binding добавлена запись Venue laptop, выполнен Save & Apply; повторное чтение подтвердило ровно одну новую запись с нужной парой IP/MAC и сохранность обеих существующих привязок. DHCP/DNS-настройки и прошивка не менялись. Проверка DNS venue по UDP/TCP после сохранения прошла. Принудительное переподключение ноутбука для проверки новой DHCP-аренды не выполнялось; проверка телефонов без WAN остаётся обязательной.
  • Локальный DNS по согласованию пользователя добавлен: CoreDNS 1.14.7 запускается с venue на TCP/UDP 53 только на VENUE_BIND_IP; оба домена возвращают этот IPv4, AAAA-запросы не получают внешних адресов. Отдельные локальные зоны не пересылаются upstream; остальные домены направляются в 1.1.1.1/8.8.8.8 при доступном WAN. DNS подключён только к сети публикации портов и не имеет доступа к внутренней сети приложения/БД. Образ закреплён по версии, скачивается при отсутствии и используется повторно; новая инфраструктурная зависимость явно согласована пользователем. Локальная .env изменена только в VENUE_BIND_IP=192.168.10.155, секреты сохранены. make venue успешно пересобрал образ из готовых файлов, выполнил миграции и перезапустил HTTPS/DNS с сохранением томов. Настройки Cudy не менялись.
  • Проверки DNS: python3 tests/venue/deploy.py, python3 tests/venue/https.py --docker, python3 tests/venue/dns.py --docker, python3 tests/venue/dns.py --server 192.168.10.155, shell-/Python-синтаксис и git diff --check прошли. Реальный CoreDNS в отдельной внутренней Docker-сети подтвердил локальные ответы по UDP/TCP и отсутствие внешних IPv6-ответов без WAN до/после перезапуска. Проверены Compose mount/порты/DNS-IP и изоляция backend; обычный внешний домен успешно разрешается через upstream на запущенной площадке. HTTPS-smoke с принудительным разрешением обоих имён только в IP ноутбука и обычной проверкой TLS подтвердил вход организатора, сохранность тестовой игры/команды/материала, SPA и отключённые email-операции. Ручное ревью diff выполнено; код Go/Vue и protobuf не менялся. Проверка DNS через DHCP Cudy и телефонов без WAN ещё требуется; IP ноутбука пока динамический.
  • Проверка реального Cudy: TR3000, прошивка 2.5.27 DE, панель доступна на 192.168.10.1; текущий DHCP-IP ноутбука 192.168.10.155 ещё не закреплён. По предоставленному пользователем файлу выполнен вход и прочитаны Custom DNS и DHCP. В этих разделах нет формы статических записей «домен → IP»: Custom DNS выбирает upstream DNS, DHCP позволяет выдавать клиентам Preferred/Alternate DNS. Настройки роутера не менялись. Для сохранения штатной прошивки предложен отдельный локальный DNS на ноутбуке с двумя записями и выдачей его адреса через DHCP; это дополнение к прежнему плану требует согласования инфраструктуры. Пользовательский pass.txt защищён режимом 600 и исключён из Git; пароль и сессионные данные не выводились. Следующий шаг — согласовать реализацию локального DNS, закрепить IP, настроить DHCP и выполнить аппаратную приёмку.
  • Выпуск сертификата по запросу пользователя начат: скачан официальный образ Certbot, отдельный ACME-аккаунт и запрос на два домена созданы в игнорируемом venue/certificates/acme/ с ограниченным доступом. Certbot ожидает добавления двух TXT-записей DNS-01 в публичный DNS; доступ к DNS в среде отсутствует, значения переданы пользователю. Сертификат ещё не выдан и не установлен; production-ключи и настройки не использованы, площадка не перезапускалась. После появления записей проверить публичный DNS, завершить валидацию, установить полную цепочку и ключ в venue/certificates/ и проверить их перед запуском.
  • Результат выпуска сертификата: после добавления пользователем TXT-записей оба значения проверены через публичный DNS, DNS-01 успешно пройден. Отдельный сертификат Let's Encrypt для обоих прежних доменов установлен в venue/certificates/fullchain.pem, ключ — в privkey.pem с режимом 600; каталог и ACME-файлы вне Git, production-ключ не использован. Срок действия до 2027-01-08 06:53:26 UTC. OpenSSL подтвердил системное доверие полной цепочке, оба домена и соответствие ключа; игнорирование обоих файлов проверено. Автопродления нет: до истечения срока повторить DNS-01 и установить обновлённые PEM-файлы. Площадка в рамках выпуска сертификата не перезапускалась; Cudy и телефоны без WAN ещё не проверены.
  • Актуальный запуск по последующему запросу пользователя: make venue всегда использует HTTPS-дополнение, выполняет docker compose build --pull=false из заранее подготовленных артефактов и up --force-recreate --no-build --pull never; тома данных сохраняются. venue-update и venue-https — алиасы единого запуска. Сертификат проверяется до Docker; Go/Vue и npm-зависимости при деплое не собираются. Отсутствующие runtime-образы и базовый Alpine скачиваются, имеющиеся не обновляются автоматически. HTTPS теперь используется smoke-проверкой по умолчанию. README и тесты приведены к единому поведению; python3 tests/venue/deploy.py, shell-синтаксис, Python-синтаксис, dry-run всех трёх Make-команд и git diff --check прошли; diff вручную проверен. Реальный перезапуск площадки с публичным сертификатом и сборка Docker-образа без WAN не выполнялись; Cudy и проверка телефонов остаются внешней контрольной точкой.
  • Уточнение сборки по запросу пользователя: деплой больше не вызывает make build, Go/Vue-сборку или установку npm-зависимостей. Артефакты заранее готовятся существующим make prod-build в обеих папках; при отсутствии бинарника, Goose, bootstrap SQL, frontend или SQL-миграций запуск завершается до Docker с понятной ошибкой. make venue-update только упаковывает готовые файлы в Docker-образ и подготавливает runtime-образы. README обновлён; тесты первого, автономного и update-запуска подтверждают отсутствие make/npm, ошибки отсутствующих артефактов и сохранность настроек. python3 tests/venue/deploy.py, python3 tests/venue/https.py, bash -n venue/deploy.sh, git diff --check прошли; diff вручную проверен. Повторная реальная сборка приложения и запуск площадки не выполнялись; код дочерних репозиториев не менялся.
  • Подготовка этапа 3: добавлены make venue-https, отдельное Compose-дополнение и Nginx-конфигурация для прежних доменов интерфейса/API. HTTP перенаправляется на HTTPS с сохранением пути и параметров; API и материалы доступны через домен интерфейса и прежний API-домен. HTTPS-дополнение задаёт публичные адреса ссылок без изменения .env. Сертификат и ключ подключаются только для чтения из игнорируемого каталога; до Docker проверяются доверие полной цепочке, оба домена, срок действия и соответствие ключа. Повторный HTTPS-запуск перечитывает сертификат без пересборки. Обычный HTTP-запуск сохранён; добавлены HTTPS-smoke и инструкции DNS-01, LAN, продления и ручной приёмки. Код Go/Vue, protobuf, схема БД и конфигурация prod не менялись; новых зависимостей нет.
  • Проверки подготовки этапа 3: python3 tests/venue/deploy.py, python3 tests/venue/https.py --docker, bash -n venue/deploy.sh и git diff --check прошли; Python-файлы проверены на синтаксис. Проверены реальный Compose merge, прежние тома и внутренняя сеть backend. Временные Docker-контейнеры с тестовым CA подтвердили проверяемый TLS/SNI, оба домена, redirects, API/материалы, SPA-пути с параметрами, перезапуск и замену сертификата через reload; свои ресурсы тест удалил. Проверки сертификатов покрывают отсутствующие/повреждённые файлы, неверный и зашифрованный ключ, недоверенную/неполную цепочку, отсутствие каждого домена и истечение срока; ошибка возникает до Docker, повторный готовый запуск не скачивает и не собирает образы, .env сохраняется. Ручное ревью diff выполнено; стандарты Go/Vue не затронуты. Реальное HTTPS-развёртывание, системное доверие телефонов, Cudy и WAN-off smoke не проверены из-за отсутствия оборудования/сертификата. Commit/push не выполнялись.
  • Подтверждение пользователя: venue работает в браузере ноутбука и на телефоне после настройки DNS. Проверка Android/iPhone при отключённом WAN и мобильных данных ещё не подтверждена.
  • Проверка ноутбука после изменения настроек роутера пользователем: системный DNS разрешает UI-домен в 192.168.10.155; HTTPS UI и API возвращают HTTP 200 с этого IP, сертификат проходит проверку. В DHCP также получен второй DNS 192.168.10.10; его назначение не проверено. Пользователь подтвердил открытие venue на телефоне, проверка без WAN ещё не выполнена. В браузере ноутбука остаётся проверить обновление DNS-кэша и использование системного DNS.
  • Результат настройки DHCP: по запросу пользователя в Cudy сохранён Preferred DNS=192.168.10.155, Alternate DNS пустой. Повторное чтение формы подтвердило значения и сохранение остальных настроек DHCP; проверка локальных доменов по UDP/TCP прошла. Получение DNS клиентами после обновления DHCP-аренды ещё не проверено.
  • Приёмка этапа 3: пользователь сообщил «все проверил работает» после контрольного списка LAN-портов, обновления DNS устройств и проверки Android/iPhone с отключёнными WAN и мобильными данными. Аппаратный блокер снят по подтверждению пользователя; агент самостоятельно эти проверки не повторял.
  • Результат этапа 4: добавлен GamePackageService для потоковой сборки и автономного чтения ZIP версии 1 из подготовленного снимка. Пакет сохраняет исходные ID, пароли QR, хеши/статусы билетов, email капитанов, сценарий с ответами, план экранов, карту команд/стола, плейлисты и все медиа. Внешние HTTP/HTTPS-медиа скачиваются с проверками адресов DNS/соединения/перенаправлений; локальные файлы доступны через адаптер существующего хранилища. Совпадающие файлы хранятся один раз. Архив и распакованный объём ограничены 1 ГиБ, файл — 64 МиБ, JSON — 8 МиБ, количество записей — 4096. Проверяются версия, связи, размеры, SHA-256, MIME, опасные пути, дубли и отсутствующие/посторонние файлы; временные каталоги и файлы имеют права 0700/0600 и очищаются на ошибке. БД, API, protobuf, зависимости и текущий архив сценария не изменены. Контракт и владение временными файлами описаны в evening_detective_server/docs/GAME_PACKAGE.md.
  • Проверки этапа 4: gofmt, go test ./internal/services/game_package_service -count=1, полный go test ./... и git diff --check прошли. Табличные тесты проверяют сохранение данных/секретов QR/билетов, оба назначения плейлистов, автономное чтение, внешние/локальные URL, дедупликацию, запрещённые адреса и redirects, повреждённые пакеты, масштабированные лимиты ZIP/распакованного объёма и отмену во время извлечения с сохранением родительского каталога. Потоковое чтение проверено на генерируемых медиа 1 и 16 МиБ с ограниченным размером чтения. Ручное ревью всего нового кода по Go-стандарту и независимое ревью проведены; замечания исправлены. Реальный пакет на 1 ГиБ, скачивание с внешнего production-хостинга и перенос игры между БД не проверялись; UI/экспорт/импорт реализуются на следующих этапах. Commit/push не выполнялись.
  • Результат этапа 5: сохранён согласованный SQL-снимок и одна активная попытка экспорта на игру. Снимок учитывает выбранную музыку в game_music_states, команды, билеты, сценарий, план экранов и карту. Пакет собирается в фоне, потоково хранится в закрытом RustFS; PostgreSQL-очередь, heartbeat 30 секунд, аренда 2 минуты, timeout 30 минут и защита от устаревшего worker сохранены. API и оба protobuf-клиента сгенерированы; права трёх ролей и запрет экспорта в venue проверены. Повторное скачивание отдаёт те же байты, обрыв не меняет состояние.
  • Изменение решения (2026-10-11): по запросу пользователя жёсткая блокировка полностью удалена; функции/триггеры БД не используются. Все обычные операции игры, команды, билеты, прогресс, музыка, карта, экраны и почтовые задания продолжают штатную работу. В списке игр и на странице управления показываются подготовка офлайн-пакета (preparing) и «Игра в офлайн-режиме» (ready). Это информационная отметка, без изменения игрового status или format; при ошибке она исчезает. Онлайн-изменения после запуска не попадают в сохранённый пакет; локальный импорт реализован на этапе 6; возврат ещё не реализован.
  • Проверка этапа 5: ранее прошли реальный тестовый RustFS-цикл с перезапуском, проверкой ZIP кодеком, приватностью объекта, потоковой выдачей/обрывом и повторным скачиванием одинаковых байтов. После упрощения прошёл полный go test ./... с PostgreSQL: снимок, редактирование в preparing/ready, конкурентный запуск, аренда, устаревшая публикация, ошибка/повторный запуск и завершение/очистка после удаления игры. Frontend type-check, lint, lint:css и Vite build в /tmp прошли; gofmt и diff проверены. Проверка интерфейса в браузере и повторный реальный RustFS-цикл в этом уточнении не выполнялись. API описан в backend docs/GAME_PACKAGE.md. Production-развёртывание, commit и push не выполняются.
  • Результат этапа 6: добавлен потоковый POST /api/games/import (сырой ZIP, JWT, роли admin/developer/organizer, только venue) и соответствующий protobuf-контракт. Кодек проверяет пакет до записи; игра, команды, билеты, сценарий, плейлисты, треки, план экранов, карта и метаданные публикуются одной транзакцией после загрузки медиа в локальный RustFS. ID игры/команд/билетов, пароли QR, хеши и состояния билетов сохранены. Сценарий/плейлисты/треки получают отдельные локальные ID; сценарий принадлежит импортирующему пользователю, исходные пользовательские ссылки не связываются с локальными аккаунтами. Исходный снимок и соответствия ID сохраняются в закрытом реестре импорта. Занятые ID/хеши и другой пакет той же игры отклоняются; повтор того же ZIP ничего не меняет, в том числе после локального редактирования или удаления игры. Реестр, heartbeat 30 секунд, аренда 2 минуты и timeout 30 минут защищают публикацию; очистка ошибок повторяется после перезапуска и не удаляет медиа при неопределённом ответе commit. Для маршрута импорта venue-прокси настроены лимит 1 ГиБ и потоковая передача; production-конфигурация не менялась.
  • Проверки этапа 6: штатные make generate и npm run proto:gen; полный обычный go test ./...; PostgreSQL-тесты импорта, экспорта и API; go test -race для imports repo/service и app; frontend type-check, lint, lint:css и Vite build в /tmp; python3 tests/venue/deploy.py, python3 tests/venue/https.py --docker и diff-проверки прошли. Тесты покрывают связи исходных ID, локальные копии, QR/билеты, пустые наборы данных, последовательности, повтор/конкурентность, конфликты до/после загрузки, изменённые байты под тем же package ID, rollback, отмену, аренду/timeout, ошибки очистки и неопределённый commit. Реальный HTTP/RustFS smoke прошёл в отдельной internal Docker-сети без WAN, с отдельными PostgreSQL/RustFS, чтением медиа, перезапуском хранилища и повторным импортом; тестовые контейнеры и данные удалены. Nginx проверен на обоих HTTPS-доменах, включая POST импорта. Ручное ревью изменений по Go/frontend-стандартам выполнено. Пакет на 1 ГиБ, реальные телефоны и обновление работающей площадки не проверялись; новая миграция 20261011120000_create_game_imports.sql требуется при обновлении. Commit/push не выполнялись.
  • Результат этапа 7: в настройках игры на подтверждённом prod доступны подготовка и скачивание пакета, состояния из game.export восстанавливаются через существующий polling; ошибка допускает повтор подготовки, готовый ZIP — повтор скачивания. Добавлены пояснения о снимке и отсутствии подтверждения импорта. На venue в списке игр добавлена Naive UI-модалка импорта одного непустого ZIP до 1 ГиБ с именем/размером; загрузка блокирует повтор, смену файла, Escape и закрытие. После импорта обновляется список, показаны обычный/повторный результат и открытие с проверкой доступности игры; удалённая игра не восстанавливается. Действия доступны admin/organizer/developer, при неизвестной среде скрыты. Бинарный клиент сохраняет JWT-refresh, отправляет исходный File с application/zip, использует generated тип импорта, поддерживает обе формы ошибок и имя из Content-Disposition; object URL освобождается. Backend/API, зависимости и маршруты не менялись.
  • Проверки этапа 7: frontend type-check, lint, lint:css, Vite-сборка в /tmp и diff-проверка прошли; ручное ревью по frontend-стандарту выполнено. Все 17 тестов node src/stores/__tests__/auth.test.ts прошли, включая ZIP-body/JWT-refresh, результат импорта, оба формата серверных ошибок, сетевую ошибку и UTF-8-имя скачивания. Firefox с подставным API на 1440/390 px проверил export polling, восстановление подготовки после обновления, ошибку/повтор и скачивание; импорт, повтор, удалённую игру, конфликт и сетевую ошибку; пустой файл, расширение, лимит; блокировки отправки/выбора/закрытия, Escape, клавиатурный focus в модалке, роли и неизвестную среду. Реальный перенос между серверами, телефоны и Blob-пакет на 1 ГиБ в этом этапе не проверялись; развёртывание не выполнялось.
  • Результат автоматической части этапа 8: добавлены повторяемые tests/venue/flow.py и flow_browser.py, команды и ручной контрольный список в README проверок. Актуальный код собирается в /tmp; стенд запускает два настоящих backend prod/venue с отдельными PostgreSQL/RustFS в Docker internal-сети без WAN, SMTP направлен в локальный sink. API проверяет оба формата, экспорт/импорт ZIP, исходные ссылки/ID/пароли команд и билетов, статусы участия, QR/PDF, обычный/повторный/конкурентный приём билетов, отказ без авторизации и неверные ссылки, локальные команды/билеты, старт/ходы/выдачу материалов/паузу/продолжение/завершение, ответы и баллы, результаты, перезапуск всех venue-сервисов и повторный импорт без потери прогресса. Проверена независимость источника и площадки; ограничения формата offline сохранены. После проверки удаляются только ресурсы стенда; ошибка очистки одного ресурса не останавливает попытки очистить остальные.
  • Исправления этапа 8: общий gameplay-сервис проверяет run до записи хода и подтверждения участия, одинаково для API/MCP; безопасная ошибка отображается как FailedPrecondition. Кнопка хода блокируется вне run и при отправке; отказ показывается пользователю и сохраняет код. Кнопкам lifecycle добавлены доступные имена. Конкурентный приём билета теперь перечитывает текущее состояние и сообщает «билет уже использован», а не ошибочно «игра уже завершена». PostgreSQL-тест конкурентности синхронизирует запросы блокировкой строки: до исправления воспроизводил ошибку, после исправления проходит.
  • Проверки этапа 8: python3 tests/venue/flow.py --browser прошёл с реальными API/хранилищем и отдельным браузерным стендом. Firefox проверил полный UI-flow на 1440 px (online) и 390 px (offline), реальное скачивание/загрузку ZIP, билеты, ходы и polling, паузу с введённым кодом, двойную отправку, отказ с сохранением ввода, выдачу материала, оценивание/результаты, завершённый экран игрока, неверный пароль и клавиатурный focus в модалке. Внутри стенда прошли PostgreSQL-регрессии gameplay/lifecycle, tickets, MCP и app; отдельно прошёл полный go test ./.... Frontend type-check, lint, lint:css и сборка в /tmp прошли; синтаксис Python, gofmt, diff-проверки, ручное ревью по Go/frontend-стандартам и независимое ревью затронутого flow выполнены. API/protobuf, миграции и зависимости не менялись. Работающие окружения и Cudy не изменялись; существующие пользовательские изменения бинарника и prod сохранены.
  • Приёмка этапа 8: пользователь подтвердил «Этап 8 принят» при согласовании следующего этапа. Отдельный повтор аппаратной проверки агентом не выполнялся.
  • Результат этапа 9: добавлен серверный сервис подготовки и проверки ZIP результатов версии 1, без новых API/UI, protobuf, миграций и зависимостей. Read-only PostgreSQL-снимок REPEATABLE READ связывает завершённую импортированную venue-игру с ID/SHA-256 исходного пакета. Сохраняются итоговые команды/участие, исходные и новые билеты, явные удаления, пароли QR/хеши билетов, порядок и время ходов/MCP, материалы и их выдача, ответы и nullable баллы. Исходные ссылки пользователей восстанавливаются из реестра; локальные исполнители вынесены в отдельное пространство. Материалы ссылаются на исходный ZIP, без локальных адресов RustFS. Изменённый игровой сценарий/формат и неизвестные материалы отклоняются. Повторная подготовка создаёт новый снимок с актуальными оценками и не меняет игру; кодек проверяет состав, версию, размеры/CRC/SHA-256, лимиты JSON/ZIP, уникальность и связи. Контракт описан в backend docs/GAME_PACKAGE.md.
  • Проверки этапа 9: полный go test ./..., unit-тесты кодека/снимка и python3 tests/venue/flow.py --results прошли. Изолированный PostgreSQL/RustFS-стенд проверил обе игры, связь с исходным ZIP, новые локальные записи, прогресс/выдачу/ответы/баллы, свежий снимок после оценивания, конкурентное оценивание, сохранность предыдущего пакета, повтор после перезапуска и отсутствие изменений исходных игр. Unit-тесты проверяют удаления, пустые наборы, повторные/неизвестные ходы/MCP, исходных и локальных исполнителей, повреждённые ZIP и связи, лимиты, отмену и ошибки записи. gofmt, Python-синтаксис, diff-проверки, ручное ревью по Go-стандарту и независимое read-only ревью выполнены. Frontend-код не менялся; его штатная сборка в /tmp прошла в составе стенда. Тестовые пакеты/контейнеры удалены; работающие окружения и Cudy не менялись. Commit/push и развёртывание не выполнялись.
  • Результат этапа 10: серверный GameResultsApplyService принимает ZIP только в prod, проверяет источник/игру/ID/SHA-256 экспорта и сохранённый исходный ZIP из закрытого хранилища. Источник команд/билетов/сценария/материалов/вопросов сверяется с ZIP, формат неизменяем. По уточнению пользователя ZIP считается источником истины: возвращаемые поля, включая онлайн-прогресс/баллы/формат и игровой сценарий, перезаписываются без сравнения с исходным или последним применённым состоянием. Изменённый сценарий восстанавливается отдельной копией для игры, не меняя сценарии других игр; исходные онлайн-ссылки материалов сохраняются. Одна SERIALIZABLE транзакция с блокировками и максимум тремя попытками при serialization failure/deadlock записывает итоговые команды/участие, билеты, удаления и связи карты, ходы/MCP, материалы/выдачу, ответы, nullable баллы, finish и фактические даты. Баллы не пересчитываются; оформление, плановая дата, музыка, карта и экраны сохраняют онлайн-изменения. Исходные ID сохраняются; новые команды сохраняют venue-ID/пароль, новым билетам/ходам/уликам выделяются онлайн-ID с реестром соответствий. Материалы возвращаются в исходные онлайн-ссылки без новых загрузок и скачивания URL. Онлайн-ссылки пользователей и технические почтовые поля сохраняются, локальные исполнители остаются в закрытом реестре; новые почтовые задания/токены не создаются, возвращённые игры исключены из очереди новых напоминаний об отзыве.
  • Реестр этапа 10: миграция game_returns сохраняет ID/хеш ZIP, экспорт, исходный ID игры, точное UTC-время подготовки с наносекундами, время применения, соответствия ID (включая сценарий), локальных исполнителей и инициатора. Реестр остаётся после удаления игры. Повтор тех же байтов возвращает сохранённую запись, включая старый пакет после нового и повтор после удаления игры; тот же ID с другими байтами отклоняется. Новый пакет требует строго более позднего created_at, без поправок часов. game.export скрывается в игре/списке после успешного возврата соответствующего экспорта; исходный экспорт остаётся ready, ZIP сохраняется. Входной ZIP проходит приватный staging, чтобы проверенные данные и SHA-256 относились к одним байтам; временные файлы 0600/0700 удаляются при успехе, отказе и отмене.
  • Проверки этапа 10: полный go test ./... с отдельным PostgreSQL, релевантный go test -race, unit-тесты источника/материалов/пользователей/безопасных ошибок/private staging, PostgreSQL-регрессии rollback/удалений/ID и хеш-коллизий/последовательностей/точных повторов/конкурентности/порядка пакетов (включая шаг 1 нс)/исправленных баллов/онлайн-редактирования/почтовых заданий прошли. python3 tests/venue/flow.py --apply прошёл для обоих форматов с настоящими venue-ZIP, prod PostgreSQL/private RustFS, исправлением баллов, прежними и новыми ссылками, сохранением оформления, исчезновением офлайн-отметки и повтором старого пакета после нового/перезапуска prod. gofmt, Python-синтаксис, diff-проверки, ручное ревью по Go-стандарту и независимое read-only ревью выполнены. Контракт обновлён в backend docs/GAME_PACKAGE.md; API/UI, protobuf и зависимости не менялись. Тестовые контейнеры/ZIP удалены; рабочие окружения и Cudy не менялись. Commit/push и развёртывание не выполнялись.
  • Уточнение этапа 10 (2026-10-11): по запросу пользователя возвращаемый ZIP — источник истины, включая игровой сценарий. Удалены сравнения с исходным/последним онлайн-состоянием и поле snapshot реестра. При отличии сценария создаётся копия для игры с исходными вопросами и онлайн-ссылками материалов; другие игры сохраняют свой сценарий, ID копии записывается в соответствиях. Проверки после изменения: полный go test ./... с отдельным PostgreSQL, race для return repo/results service/export service, flow.py --apply обоих форматов с перезапуском prod, gofmt, diff-проверка и ручное ревью по Go-стандарту прошли. PostgreSQL-регрессии проверяют перезапись онлайн-команд/ответов/баллов/формата, удаление добавленных онлайн-команд, смену/правки общего сценария и отсутствие повторного копирования неизменённого сценария. Онлайн-правки возвращаемых данных теряются намеренно; проверки источника, порядка пакетов, коллизий и атомарность сохранены.
  • Ограничение этапа 10: подлинность ZIP криптографически не подтверждается. nextval PostgreSQL не откатывается: большие разрывы venue-ID расходуют sequence даже при отказе. Продвижение выполняется пакетами с пределом пять секунд, затем данные откатываются и выдаётся безопасная повторяемая ошибка; INT32-потолок отклоняется. Перед поддержкой таких площадок требуется расширение типов ID; лимит отмечен shortcut: в коде и контракте.
  • Результат этапа 11 (2026-10-11): подключены сервисы подготовки/применения, protobuf/OpenAPI и отдельные HTTP handlers results-package/download и results-package/apply. Venue выдаёт свежий ZIP завершённой импортированной игры после полной подготовки в файле 0600; prod принимает ограниченный поток до 1 ГиБ без обязательного Content-Length. Положительный ID и совпадение с manifest проверяются до повтора и изменений; временные файлы очищаются. Ответ содержит только gameID/packageID/appliedAt; безопасные ошибки 400/413/409/503, права организатора/администратора/разработчика. В настройках игры добавлены скачивание и применение ZIP, явное предупреждение о перезаписи данных/сценария и сохранении оформления/музыки, клиентская валидация, обновление авторизации, загрузка и блокировка повторного запроса. После успеха страница пересоздаётся и перечитывает данные без старых ответов/черновиков; повтор сообщает «Результаты приняты». Возврат остаётся доступен после снятия офлайн-отметки. Обновлены репозиторные proxy configs (1 ГиБ/30 минут), контракт и изолированный стенд; работающие серверы не менялись.
  • Проверки этапа 11: штатная генерация обоих репозиториев, gofmt, go test ./..., PostgreSQL/race-регрессии return repo/results/export service и HTTP поверх реального сервиса, frontend type-check/lint/lint:css/build, diff-проверки и ручное ревью по обоим стандартам прошли. Независимое ревью не выявило замечаний. python3 tests/venue/flow.py --apply --browser прошёл полный цикл реальных API/PostgreSQL/RustFS/Firefox для online (1440 px) и offline (390 px): кнопка свежего ZIP → страница prod → результаты, прежние ссылки/билеты, сохранённые название/описание, снятая офлайн-отметка, исправленный балл, перезаписанный онлайн-ответ, повтор старого ZIP после нового и перезапуска. Проверены чужой ID до/после применения, права, MIME, заявленный размер, поток без Content-Length, безопасные ошибки и очистка файлов. Аппаратный прогон на телефонах не повторялся.
  • Результат этапа 12 (2026-10-11): добавлены потокобезопасный сервис присутствия в памяти, wiring, protobuf/OpenAPI POST /api/devices/heartbeat и GET /api/devices с фильтром игры. Проверяются UUID, взаимоисключающие контексты, JWT/существующие права игры, пароль команды и неотменённое участие без загрузки сценария/прогресса, существующая сессия экрана; ID игры команды/экрана определяет сервер. Реестр хранит только проверенные связи, UUID, серверные времена и последние IP/User-Agent; credentials не сохраняются и не выдаются. Полная замена/очистка контекста, граница связи строго менее 60 секунд, удаление через 15 минут при обновлении/чтении, предел 1024 с ResourceExhausted без вытеснения существующих записей. В prod heartbeat явно возвращает enabled=false, список недоступен.
  • Браузер этапа 12: общий heartbeat в App сразу/каждые 20 секунд без параллельных запросов, при видимости/восстановлении сети и смене контекста. UUID профиля отдельно от авторизации в localStorage с временным fallback, UUID подключения на экземпляр приложения. Контекст страницы очищается при уходе, отозванный lifecycle не может восстановить старые связи; смена игры/пароля пересоздаёт page-компонент. Generated client и обновление JWT организатора переиспользованы; публичные credentials команды/экрана не запускают обновление JWT. После enabled=false polling прекращается. Ошибки не показывают toast и повторяются по интервалу. UI списка устройств не добавлен.
  • Проверки этапа 12: штатная генерация обоих репозиториев, gofmt, go test ./..., race для presence service/app, frontend type-check/lint/lint:css/build, self-check UUID/store/composable и diff-проверки прошли. Unit-тесты с управляемыми часами покрывают повтор/вкладки/смену и очистку связей, границы 60 секунд/15 минут, лимит и конкурентное чтение/обновление; API-тесты — роли, режимы, safe errors и отсутствие секретов. python3 tests/venue/flow.py --browser прошёл изолированные реальные API/PostgreSQL/RustFS/Firefox: credentials/отменённое участие, UUID/поля, группировку вкладок игрока/организатора/экрана, фильтр и привязку экрана, SPA-навигацию, прекращение heartbeat и восстановление сети, повторную регистрацию штатным интервалом после перезапуска backend. Независимое ревью выявило два замечания (публичный heartbeat не должен обновлять JWT, обрезка User-Agent должна сохранять UTF-8); оба исправлены и покрыты регрессиями. Итоговое ручное ревью по Go/Frontend стандартам выполнено; замечаний не осталось.
  • Результат этапа 13 (2026-10-11): venue-вкладка «Состояние» /games/:id?tab=state показывает серверные connected/serverTime, количество доступных устройств/устройств без связи/подключений, UUID профиля, последние обращения и раскрываемые подключения с типом, ID команды/экрана, статусом, IP и User-Agent. Нет связи идёт первым, затем UUID; непривязанные браузеры исключает фильтр игры. Generated GetDevices вызывается сразу и каждые 5 секунд через useSimplePolling только на активной вкладке, без параллельных запросов; поздние ответы после смены игры, ухода и уничтожения игнорируются. Первичная загрузка, пустой список, встроенная ошибка, сохранение предыдущего снимка/времени и повтор реализованы без повторных toast. Вкладка доступна с клавиатуры. Игровые показатели, Cudy и история присутствия не добавлялись.
  • Доступ этапа 13: GetGames/GetGame и UI permissions расширены для разработчика только в venue. Разработчик без организатора/администратора/автора получает явный whitelist ID/названия/описания/формата/места/планового начала/статуса; GetGame использует краткое чтение без сценария/команд. UI скрывает создание и результаты, показывает название и только состояние, нормализует другие tab, не загружает экраны/музыку/план/карту/ответы; heartbeat остаётся браузерным. Дополнительные роли, импорт и запреты игровых действий сохранены; prod новые права не получает. При смене роли полный поздний GetGame не применяется, приватные баннер/модалки скрываются.
  • Проверки этапа 13: gofmt, полный go test ./..., race-тесты device_presence_service/app с управляемыми часами этапа 12; frontend type-check/lint/lint:css/build и self-check gameState прошли. Self-check покрывает сводку/сортировку, активность, отсутствие параллельных запросов, поздние ответы, ошибку/восстановление. Изолированные PostgreSQL/RustFS API-регрессии и Firefox проверили организатора, три подключения одного профиля, клавиатуру, смену контекста, пустой список, потерю/восстановление GetDevices, остановку запросов на другой вкладке, разработчика venue/prod, безопасные GetGame/GetGames и отсутствие закрытых фоновых запросов. API/protobuf/миграции/зависимости не менялись. Ручное ревью diff по Go/frontend-стандартам и независимое read-only ревью выполнены; пользовательские изменения prod/reproxy/backend-бинарника сохранены. Работающие окружения и Cudy не менялись.
  • Результат этапа 14 (2026-10-11): отдельный процесс того же backend-бинарника --collect-cudy читает текущих Wi-Fi-клиентов Cudy через штатный вход и GET страницы клиентов раз в 10 секунд. Проверен настоящий TR3000 V1 2.5.27-20260804-183625; настройки не менялись. Собираются только MAC/IP/диапазон/доступный сигнал dBm, без имён, трафика и истории. Пароль читается из локального файла, не выдаётся API/логами; venue/cudy/ исключён из Git. URL ограничен числовым private/loopback IP, redirects тем же host/scheme, ответ 2 МиБ, HTTP timeout 5 секунд, сессия переиспользуется и восстанавливается. Неизвестная разметка вызывает ошибку вместо ложного пустого списка. Collector изолирован в entrance без БД/RustFS; backend сохраняет внутреннюю сеть и читает общий атомарный снимок read-only. Без credential-файла обращений к роутеру нет.
  • Результат автоматической части этапа 15: Wi-Fi-блок вкладки состояния показывает весь Cudy независимо от игры, MAC/IP/диапазон/dBm или «Нет данных», число клиентов, последний успешный снимок, ошибку/устаревание. При ошибке collector сохраняет прежние данные и время, включая перезапуск; свежесть 30 секунд считает сервер, история не сохраняется. MAC не связывается предположениями с UUID браузера. Существующий GetDevices расширен Wi-Fi-полем с теми же venue/role guards, нового endpoint/миграций/зависимостей нет. Обновлены protobuf/OpenAPI и оба generated clients.
  • Проверки этапов 14–15: gofmt, go test ./..., race Cudy/wifi/presence/app, frontend type-check/lint/lint:css/build и self-check состояния прошли. Mock Cudy проверяет hash/CSRF/session/readonly GET, DOM/оба диапазона/сигнал/неизвестную разметку; снимок проверен атомарно и с управляемыми часами; CLI self-check подтвердил сохранение клиентов/времени после перезапуска при недоступном роутере. Firefox выявил прежнюю гонку раннего возврата ZIP до загрузки ID игры; действия передачи/возврата теперь появляются только после загрузки положительного ID. Итоговый изолированный Firefox-прогон обоих форматов (1440/390 px) прошёл: Wi-Fi fixtures, устройства/роли/polling и полный цикл ZIP до повторного возврата после перезапуска. Полный API-flow с PostgreSQL/race-регрессиями также прошёл; временные стенды удалены. load.py прошёл на отдельном Docker-стенде без WAN: 100 виртуальных профилей, 25 команд, 60 секунд, 300 heartbeat, 1200 story, 50 ходов, 25 ответов и 12 опросов состояния; ровно 100 доступных устройств и ноль HTTP-ошибок. p95: heartbeat 118 мс, story 68 мс, move 109 мс, answers 45 мс, devices 138 мс (бюджет теста 2 с). Нагрузка backend идёт по HTTP напрямую; DNS/HTTPS/Nginx проверены отдельно dns.py --docker/https.py --docker. Ручное ревью по стандартам и независимое read-only ревью выполнены; замечание о ложном пустом DOM исправлено и покрыто регрессией.
  • Закрытие (2026-10-11): пользователь явно поручил закрыть задачу после сообщения об оставшихся проверках. ED-029 перенесена в архив по результатам реализации и автоматических проверок; аппаратная приёмка не считается выполненной. Непроверенные пункты: реальные Android/iPhone без WAN на обновлённой версии, 100 настоящих Wi-Fi-устройств, покрытие зала и фактический ZIP 1 ГиБ. Чек-лист сохранён в tests/venue/README.md. Всего 15 этапов; ZIP остаётся неподписанным. Развёртывание, commit/push не выполнялись.

ED-030 — Индикатор среды сервиса в интерфейсе

  • Статус: DONE
  • Репозитории: evening_detective_server, evening_detective_frontend.
  • Связь: дополнение к ED-029; не требует готовности DNS/HTTPS и Cudy.
  • Цель: пользователь видит, к какой среде подключён интерфейс: PROD или VENUE.
  • Решение: показывать компактную подпись среды в общем интерфейсе рядом с навигацией; получать среду от подключённого бэкенда по фактическому режиму запуска (APP_MODE=venue — площадка, штатный онлайн-режим — production).
  • Критерии: подпись видна на компьютере и телефоне; PROD и VENUE различаются текстом, а не только цветом; среда определяется сервером, а не адресом сайта или настройкой сборки фронтенда; при загрузке или ошибке получения среды интерфейс не показывает неподтверждённый PROD/VENUE.
  • Ограничения: без переключения серверов, изменения игровых правил, показа секретов, новых зависимостей и изменения папки prod; использовать существующую навигацию, API и стандарты обоих репозиториев.
  • Проверка: ручной прогон обоих режимов и недоступного API; Go-тесты; frontend type-check, lint, lint:css и build; при изменении protobuf — штатная генерация обоих клиентов.
  • Результат: поле environment добавлено в существующий публичный GetPermissions API; значение определяется тем же режимом запуска бэкенда, который отключает внешние интеграции площадки. В HeaderMenu добавлены русские подписи «Сервер» (PROD) и «Локальная сеть» (VENUE) через Naive UI; загрузка, неизвестная среда и ошибки не подменяются production. Сведения меню хранятся в общем UI-store; устаревшие ответы не заменяют новые, а права не применяются после смены токена.
  • Проверки: штатные make generate и npm run proto:gen, gofmt, go test ./..., npm run type-check, npm run lint, npm run lint:css, npm run build и git diff --check прошли. Табличный Go-тест проверяет оба режима для гостя, пользователя и администратора. Firefox headless с подменой API проверил PROD/VENUE до и после входа, загрузку, серверную и сетевую ошибки, неизвестное значение; подпись находится внутри viewport при ширине 1440 и 390 px.
  • Ревью: diff проверен по стандартам обоих репозиториев; зависимости не добавлены, generated-файлы получены штатной генерацией. Сборочные изменения dist исключены из diff; папка prod и существующие локальные артефакты бэкенда не менялись.
  • Ограничение проверки: реальные Android/iPhone и развёртывание обновлённой версии не проверялись; для появления индикатора нужно обновить бэкенд и фронтенд. Миграция БД не требуется. Commit и push не выполнялись.

ED-028 — Роль разработчика и общий таскменеджер

  • Статус: DONE
  • Репозитории: evening_detective_server, evening_detective_frontend
  • Результат: добавлены роль developer, выдача и отзыв роли на странице пользователей, право tasks_page и общая доска /tasks для разработчиков и администратора. Реализованы четыре статуса, создание и редактирование, назначение разработчика, необязательная календарная дата завершения, фильтр по ответственному, перенос карточек и удаление с подтверждением. PostgreSQL хранит задачи; версии защищают от конкурентной перезаписи, форма сохраняет черновик при конфликте. При отзыве роли прежнее назначение сохраняется; при удалении пользователя задача сохраняется без ссылок на него.
  • Стандарты: ручное ревью всего diff по GO_STANDARD.md и FRONTEND_CODE_STANDARD.md выполнено; новые зависимости не добавлены; API сгенерирован штатными командами; сборочные изменения dist исключены из diff.
  • Проверки: make generate, npm run proto:gen, стандартный go test ./..., npm run type-check, npm run lint, npm run lint:css, npm run build и git diff --check пройдены. Интеграционный go test ./internal/repos/tasks_repo -count=1 -v с отдельной TEST_DATABASE_URL пройден: ограничения БД, назначения, смена статусов, конкурентные записи, удаление пользователей и миграция Up/Down. Шесть тестов дедлайна пройдены в Asia/Barnaul и America/Los_Angeles.
  • Проверка браузера: Firefox headless с подменой API — создание, назначение ответственного и даты, редактирование, сохранение черновика при конфликте, перетаскивание, подтверждение удаления и отказ пользователю без роли; мобильная ширина 380 px, горизонтальная прокрутка только внутри доски. Полный сценарий с работающим production-сервисом не запускался.
  • Известное ограничение проверки: дополнительный полный go test ./... с TEST_DATABASE_URL обнаружил существующие ошибки игровых фикстур в answer_service, game_service, gameplay_service, participation_service (временные таблицы не соответствуют текущей схеме). Эти тесты не менялись; стандартный полный прогон и новые интеграционные тесты проходят.
  • Запуск: применить миграцию 20261007130000_create_developer_tasks.sql штатным процессом миграций и выдать пользователю роль на странице /users; после нового входа доступна /tasks. Production-миграции, commit и push не выполнялись.

ED-020 — Музыка из плейлиста на экранах игры

  • Статус: DONE
  • Приоритет: P1
  • Репозитории: evening_detective_server, evening_detective_frontend
  • Результат: добавлены общие MP3-плейлисты и треки, назначение default-плейлиста сценарию и выбор музыки для игры. Состояние музыки хранится на уровне игры и синхронизируется через существующий polling экранов; добавлены play/pause/stop, выбор трека, громкость, loop, автопереход и единственный экран со звуком. Добавлены UI управления во вкладке «Экраны» и выбор плейлиста в редакторе сценария.
  • Проверки: GOCACHE=/tmp/evening-detective-go-cache go test ./..., npm run type-check, npm run lint, npm run lint:css, npm run build и git diff --check пройдены; protobuf сгенерирован штатными командами.
  • Ручная проверка: не выполнялась в среде агента; требуется проверить загрузку MP3 от ролей, воспроизведение на двух экранах, автопереход, loop и переподключение.

ED-027 — Упростить экран игры организатора

  • Статус: DONE
  • Приоритет: P1
  • Репозиторий: evening_detective_frontend
  • Результат: админский экран игры разделён на вкладки «Игра», «Команды», «Экраны» и «Карта». Вкладка «Игра» открывается по умолчанию и содержит статус, таймер, управление состоянием и служебные действия; выбранная вкладка хранится в query-параметре tab и восстанавливается после обновления. Существующие панели, API и игровая логика сохранены.
  • Проверки: npm run type-check, npm run lint, npm run lint:css, git diff --check пройдены.
  • Ручная проверка: не выполнялась в среде агента; требуется проверить переключение вкладок и основные действия на реальном экране организатора, включая узкую ширину.

ED-015 — План экранов игры для организатора

  • Статус: DONE
  • Приоритет: P1
  • Репозитории: evening_detective_server, evening_detective_frontend
  • Результат: добавлен необязательный план экранов игры с линейными шагами, текущим индексом и параметрами текста и таймера. Организатор может создать, изменить, удалить и переставить шаги, а также перейти на любой шаг; состояние применяется ко всем подключённым экранам и повторно применяется при привязке нового экрана. Добавлен текстовый режим экрана. Игры без плана продолжают работать через существующее ручное управление; синхронизация использует текущий polling.
  • Проверки: целевые backend-пакеты и screen_plan_service проходят; frontend npm run type-check, npm run lint, npm run lint:css, npm run build и git diff --check пройдены; protobuf сгенерирован штатными командами. Полный backend go test ./... дополнительно упирается в sandbox-запрет listen tcp в gateway-тестах.
  • Ручная проверка: не выполнялась в среде агента; требуется проверить реальными экранами создание плана, переходы, синхронизацию нескольких дисплеев и переподключение.

ED-016 — Привязка экрана к игре через сканер

  • Статус: DONE
  • Репозитории: evening_detective_server, evening_detective_frontend
  • Результат: на странице игры добавлено сканирование QR-кода экрана с немедленной привязкой к текущей игре; список экранов показывает онлайн/оффлайн-состояние и предупреждает о недоступности более минуты. Сервер разрешает управление организатору и администратору, корректно перепривязывает экран и переносит текущее состояние целевой игры.
  • Проверки: GOCACHE=/tmp/evening-detective-go-cache go test ./..., npm run type-check, npm run lint, npm run lint:css, git diff --check пройдены.

ED-026 — Количество игроков при создании команды из панели игры

  • Статус: DONE
  • Приоритет: P1
  • Репозитории: evening_detective_server, evening_detective_frontend
  • Результат: в форме создания команды на GamePage добавлено количество игроков со значением 1 по умолчанию и диапазоном 1–8. Значение передаётся через AddTeam и сохраняется в существующем teams.participants_count; старые flow без значения сохранены.
  • Проверки: go test ./..., npm run type-check, npm run lint, npm run lint:css, npm run build пройдены; protobuf сгенерирован штатными командами; git diff --check пройден.

ED-023 — Оценка сложности игры в отзыве и каталоге

  • Статус: DONE
  • Приоритет: P1
  • Репозитории: evening_detective_server, evening_detective_frontend
  • Результат: в отзыв добавлена обязательная оценка сложности 1–3; значение хранится в существующей таблице отзывов и передаётся через protobuf. Средняя сложность считается по одобренным отзывам завершённых игр сценария и отображается как «лёгкая», «средняя» или «тяжёлая» в каталоге и карточке сценария. Старые отзывы поддержаны значением по умолчанию 2; рейтинг 1–5, модерация и запрет повторного отзыва сохранены.
  • Проверки: go test ./... пройден с кэшем в /tmp; npm run type-check, npm run lint, npm run lint:css пройдены; protobuf сгенерирован штатными командами; git diff --check пройден.

ED-012 — Строгий стандарт написания фронтенда

  • Статус: DONE
  • Репозиторий: evening_detective_frontend
  • Результат: создан обязательный стандарт docs/FRONTEND_CODE_STANDARD.md как единственный источник правил структуры, компонентов, composables, функций, типов, API, состояния, стилей, форм, доступности и проверок. Фронтендный AGENTS.md оставлен указателем и операционными правилами, а корневой AGENTS.md содержит условную ссылку для задач фронтенда.
  • Проверки: проверены ссылки и однозначное размещение типовых видов кода; git diff --check пройден; продуктовый код и зависимости не менялись.

ED-025 — Доступ к БД только через repository

  • Статус: DONE
  • Репозиторий: evening_detective_server
  • Результат: прямой доступ к БД из application flow убран; проверка доступности БД вынесена в health_repo, screen_sessions_repo — в screen_service, а game_map_repo — в map_service. Handlers используют сервисные модели и больше не импортируют эти repositories.
  • Проверки: целевые backend-пакеты компилируются; internal/app проходит тесты; git diff --check пройден. Полные gateway-тесты в sandbox заблокированы запретом на listen TCP-порта.

ED-011 — Строгий стандарт написания Go-бэкенда

  • Статус: DONE
  • Репозиторий: evening_detective_server
  • Результат: добавлен обязательный стандарт docs/GO_STANDARD.md как единственный источник правил Go-кода: слои, handlers, services, repositories, modules с обязательными интерфейсами, mapping, protobuf, ошибки, SQL, транзакции, тесты и именование. В backend AGENTS.md оставлены только ссылка на стандарт, проектные ограничения и операционные команды.
  • Проверки: go test ./... пройден; git diff --check пройден.

ED-019 — Добавить кнопку экрана в меню организатора

  • Статус: DONE
  • Репозиторий: evening_detective_frontend
  • Результат: в HeaderMenu.vue добавлен пункт Экран для пользователей с правом games_page; пункт ведёт на существующий маршрут /screen.
  • Проверки: npm run type-check, npm run lint, npm run lint:css пройдены.

ED-001 — Исправить actions_count в GetGameAnswers

  • Статус: DONE
  • Репозиторий: evening_detective_server
  • Результат: GetGameAnswers теперь заполняет Team.ActionsCount через GetActionsCountByTeamIDs; контракт API не изменён.
  • Проверки: go test ./... пройден; lifecycle-тест проверяет количество ходов в ответе для панели оценки.

ED-011 — Считать сообщения без тригерной фразы продолжением диалога

  • Статус: DONE
  • Репозиторий: корневые правила агента
  • Результат: удалено обязательное ожидание тригерной фразы перед продолжением диалога.
  • Проверки: проверены AGENTS.md и docs/agent/README.md; продуктовый код не затрагивался.

ED-005 — Исправить сброс настроек игры поллингом

  • Статус: DONE
  • Репозиторий: evening_detective_frontend
  • Результат: настройки редактируются через локальный черновик и не перезаписываются поллингом.
  • Проверки: type-check, lint и lint:css пройдены.

ED-006 — Добавить страницу итогов игры

  • Статус: DONE
  • Репозиторий: evening_detective_frontend
  • Результат: добавлена страница результатов, обработка ошибок и входы из админки и списка игр.
  • Проверки: type-check, lint, lint:css и build пройдены.

ED-007 — Добавить пьедестал победителя на странице итогов

  • Статус: DONE
  • Репозиторий: evening_detective_frontend
  • Результат: добавлены баннер победителя, медали мест 1–3 и обработка ничьих.
  • Проверки: type-check, lint, lint:css и build пройдены.

ED-008 — Добавить создание игры из списка игр

  • Статус: DONE
  • Репозиторий: evening_detective_frontend
  • Результат: добавлено создание игры из каталога сценариев, выбор даты и времени, отображение даты и исправлена передача RFC 3339 startAt.
  • Проверки: type-check, lint и lint:css пройдены; build указан в отчёте плана.