Skip to content

Архитектура кода, диагностики и тестов VOCA не закреплена исполняемым фундаментом #13

Closed

Description

@prineycom

Актуальная доработка по замечаниям инженера — 2026-09-11

Доработка той же VOCA-13, в ветке VOCA-13 и открытом PR #14. Это не новый тикет и не реализация голосового этапа. Требования ниже уточняют организацию первоначального фундамента и заменяют противоречащие им пути/структуру первого плана; остальные требования к поведению, диагностике, надёжности и экономии тестов сохраняются.

Замечание инженера

ну пока организация кода выглядит очень плохо. Я вижу файлы огромные содержащие много кода и методов. Давай для бэкенда используем Simple Clean Architecture подход а для веба FSD подход. При этом нужно явно разделить backend и frontend, а то сейчас все намешано в кучу.

После анализа причин и применимости инженер подтвердил: «давай обновляй задачу и приступай к plan и затем /do по применению правок».

Проблема и подтверждённая причина

На исходном для доработки commit c127d19f6f0d2d94b8a16514eec4c3a7adef3562 рабочая session-основа есть, но границы ответственности выражены недостаточно:

  • src/voca/adapters/control.py (374 строки) объединяет HTTP bootstrap, WS lifecycle, schemas/validation, routing команд, wire responses и deadlines. На строке 115 transport вызывает service.store.get, обходя application boundary.
  • src/voca/core/session.py совмещает сущность, storage port, сценарии, ошибки и parsing wire cursor eN.
  • src/voca/diagnostics.py (222 строки) совмещает configuration, formatter/filter/rotation/failure policy и instrumentation операций.
  • web/src/App.tsx (289 строк) совмещает UI, storage, socket ownership, state projection/dedup и пользовательские сценарии; web/src/control.ts смешивает HTTP, WS, команды, wire types и decoding.
  • Backend packaging/src/tests находятся в корне, frontend внутри web/: границы двух приложений структурно несимметричны.

Число строк — свидетельство мест для анализа, не квота и не единственный критерий. Простое перемещение всего App в один большой hook или разрезание файла по N строк не решает проблему.

Согласованные рекомендации к реализации

  1. Явные приложения backend/ и frontend/ в одном репозитории. Перенести Python src/tests/pyproject/uv.lock/.python-version в backend/, frontend web/ в frontend/ с package.json/pnpm-lock.yaml. Общие docs, CI и root README остаются общими. Обновить команды, paths, ignores, CI, bootstrap и documentation. Python — uv, frontend — pnpm; без нового workspace/build orchestrator или второго бизнес-backend без потребителя. Между приложениями только HTTP/WS-контракт.

  2. Backend: Simple Clean Architecture. domain — сущности/независимые правила; application — use cases и узкие требуемые порты; presentation — HTTP/WS schemas/validation/transport/error mapping; infrastructure — SQLite и конкретная диагностика; bootstrap — composition/lifespan. Исходные зависимости направлены внутрь. Domain не знает FastAPI, SQL, handlers, файлов логов и wire envelopes; application не импортирует конкретную инфраструктуру. Transport вызывает use cases, не repository через service.store. Wire parsing/mapping остаются снаружи; внутри проверенные простые значения. Resource open/close — ответственность composition, не бизнес-порта. Для instrumentation необходимый маленький контракт, без зависимости domain от реализации логирования.

  3. Декомпозиция backend по ответственности. Разделить HTTP и WS входы, decoder/schema, wire presentation, прикладные сценарии, persistence и instrumentation/configuration там, где меняются по разным причинам. Связанный небольшой код оставлять вместе. Не требовать отдельного класса/файла на каждую функцию, Input/Output/Interactor/Presenter wrappers на каждый вызов. Не вводить generic Repository/UoW, DI/plugin framework, CQRS/event bus и пустые реализации будущих adapters.

  4. Frontend: Feature-Sliced Design. Для существующего экрана начать с app, pages/session-console и только необходимого shared. В page slice разделить ui, model, api и действительно нужный lib; публичный API слайса. App только собирает приложение. UI не владеет HTTP/WS/localStorage. Применение событий/проверка cursor отделены от транспорта; lifecycle socket имеет одного владельца, callbacks старого socket не меняют новое состояние. Не переносить всю сложность App в один giant hook. Слайсы не импортируют внутренности соседних слайсов; зависимости идут на нижние слои через public API. Shared не склад session-domain логики. Entities/features добавляются по реальной ответственности/повторному использованию, не ради полного дерева FSD; не создавать пустые widgets/processes или feature на каждую кнопку. React/TypeScript/Vite/pnpm сохраняются, новый state manager/UI-kit не требуется.

  5. Сохранить поведение и диагностику. Не менять публичные маршруты, wire envelope/error/cursor semantics, SQLite schema, session identity/recovery, состояние off/disconnected и существующие logging guarantees ради реорганизации. Backend/frontend separation не должно подменять старую БД пустой из нового cwd: явно описать и проверить сохранение выбранного VOCA_DB_PATH, не перемещать/удалять пользовательские данные автоматически. Startup/shutdown, cancellation/timeout, correlation, WARNING для rejected commands, bounded safe logs и все исправления первого review должны сохраниться. LangSmith/gates и будущий голос по-прежнему не входят.

  6. Тесты сохраняют поведение, не зеркалят слои. Перенести и адаптировать существующую meaningful real-SQLite suite; не удалять/ослаблять проверки и не создавать копию на каждом слое. Добавлять focused tests только для самостоятельных правил/реального риска (например event projection/cursor). Минимальная проверка запрещённых imports/public API допустима готовым инструментом или короткой узкой проверкой, но не собственный architecture testing framework. Не создавать mock infrastructure, coverage/LOC квоты и набор harnesses ради структуры. На review проверить нет ли новых бессодержательных обёрток, чрезмерно раздробленных файлов, giant modules/hooks, unsafe cross-imports и дублирования тестов.

  7. Синхронизировать архитектурные документы. Обновить code architecture/testing/diagnostics/ADR/README/roadmap и карту всей очереди VOCA-4–12/1 под Simple Clean + FSD + backend/frontend. Исходные системные контракты VOCA-2 не переизобретаются. Изменение принятого решения фиксировать честно в ADR; не выдавать первый непрошедший приёмку layout за окончательно принятый. Не переписывать исторический ADR-0001.

Дополнительная приёмка доработки

  • Открыв корень, инженер видит backend/ и frontend/ с собственными зависимостями/конфигами/командами; код приложений не остаётся в прежних src/tests/web.
  • По HTTP → use case → storage и WS → use case → response можно пройти без обхода application boundary; domain/application не импортируют FastAPI/SQLite/logging handlers и другую конкретную infrastructure.
  • В frontend App только собирает страницу; presentation, модель применения событий, API/transport и persistence имеют ясные границы. FSD layers/slices/public API и направление imports соответствуют документам; нет пустых слоёв для вида.
  • Создание, resume/reconnect, repeat command, restart с тем же DB path, cursor и error behavior не регрессировали; SQLite schema и wire contract сохранены. Browser/live walkthrough проводится на /review, а /do не объявляет его пройденным по API/build.
  • Correlation/terminal outcomes, безопасные ошибки, logging level/rotation/degraded behavior сохранены и подтверждены существующими/узкими необходимыми проверками.
  • Полный актуализированный format/lint/type/test/build/CI набор зелёный; осмысленные тесты первого раунда не ослаблены. Нет искусственного требования увеличивать число тестов вместе с числом файлов.
  • Независимое review проверило именно распределение ответственности и import boundaries, а не только прохождение проверок. Декомпозиция не свелась к переименованию папок или giant hook.
  • Документы и команды соответствуют реальной новой структуре. Все изменения отправлены в тот же PR Separate session backend and frontend architecture #14; инженер получает его повторно на review. Merge и следующие задачи не запускаются.

Основания архитектурных решений


Исходный полный объём фундамента — сохраняется с уточнениями выше

Проблема

Согласованная в VOCA-2 (#2, PR #3) архитектура описывает системные контракты, но ещё не закреплена организацией исполняемого кода, диагностикой и экономной архитектурой тестов. Начало реализации всей очереди без этой основы создаёт риск сильной связанности компонентов, непрозрачных ошибок и тестовой инфраструктуры, которая становится сложнее полезного кода.

Ожидается: расширяемая, поддерживаемая и стабильная архитектура с заменяемыми компонентами, понятными точками диагностики и обоснованными тестами. Инженер должен самостоятельно просмотреть документацию и работающую основу в одном PR до продолжения голосовой реализации.

Причина

В main f1005a90bc2df365937bdb6c8d68b32963699f31 находятся восемь Markdown-документов и notification workflow. Прикладного кода v3, зависимостей, конфигурации запуска и тестов ещё нет. Системные контракты уже определяют владельца turn, provider interfaces, cancellation, control protocol и durable delivery; пробел — перевод этих решений в модули, граф зависимостей, управление ресурсами и проверяемую композицию.

Это не диагноз плохого существующего кода v3: его нет. Не требуется исследовать или переносить v2.

Путь

Одна фундаментальная задача перед VOCA-4. Анализ охватывает всю очередь: #4, #5, #6, #7, #8, #9, #10, #11, #12 и существующий эксперимент #1. Реализуется только согласованный ниже фундамент, не вся очередь.

1. Архитектура кода

  • Один Python/FastAPI backend; явная сборка приложения и направление зависимостей. Прикладная логика не импортирует FastAPI/FastRTC, SDK внешних провайдеров или конкретный SQLite adapter.
  • Небольшие контракты на реально заменяемых границах: transport, STT/LLM/TTS, внешние агенты, хранение и tracing. Различия capabilities, ошибок, streaming/cancel не скрываются под обещанием совместимости по URL.
  • Каждый слой и абстракция объясняются конкретным потребителем или задачей очереди. Будущая задача оправдывает описание точки расширения, но не создание неиспользуемого Protocol, пустого класса или fake adapter сейчас.
  • Не вводить универсальный Repository[T], общий agent gateway, plugin framework, DI-контейнер, брокер или вторую бизнес-state-machine в UI.
  • Для состояния и ресурсов определены владельцы, lifecycle, startup/shutdown, границы транзакций, конкурентный доступ, timeouts, cancellation, late events и restart recovery.
  • Голосовая generation, внешние поручения и tracing имеют независимые области отказа/отмены. Barge-in не отменяет поручение; trace outage не должен останавливать микрофон. Не объединять всё в один fail-fast TaskGroup и не ждать завершения старого inference перед новым capture.
  • Составить карту всех задач очереди: затрагиваемые модули, сохраняемые границы, будущие проверки. Не переписывать заново все документы VOCA-2 и не реализовывать будущие функции.
  • Документы архитектуры кода, тестирования и диагностики, ADR с согласованными решениями/альтернативами, актуальная навигация README/index и место фундамента в roadmap должны соответствовать реальному каркасу.

2. Минимальный исполняемый каркас — согласован первый вариант

  • Python/FastAPI, pyproject.toml, один Python lockfile; предложенный и включённый в согласованный пакет инструмент Python — uv. Воспроизводимый bootstrap, без установки зависимостей в системный Python.
  • React + TypeScript + Vite; пакетный менеджер frontend — pnpm, с pnpm-lock.yaml. Пока без отдельного state-management framework и UI-kit. Vite — средство разработки/сборки, не второй бизнес-backend.
  • Небольшая диагностическая панель: создать сессию, увидеть её ID/состояние, переподключиться и восстановить существующую сессию.
  • Настоящий путь: HTTP → прикладная логика → SQLite → WebSocket session.snapshot → восстановление после перезапуска backend.
  • Реальные startup/shutdown, минимальная достаточная схема хранения и узкая используемая storage boundary. Не создавать будущую task/delivery модель заранее.
  • Реализованное подмножество control protocol явно описано. Repeat resume не создаёт новую session. Невалидный ввод, неизвестный ID и неподдерживаемый start дают явные ошибки.
  • Control WS сам по себе не означает готовое голосовое соединение: conversation остаётся off, без WebRTC нельзя объявлять voice connection connected/listening. Не создавать фиктивные messages/turns/tasks или audio.
  • Нет synthetic turn, scripted LLM, STT/LLM/TTS/Pi/LangSmith adapters и их заглушек ради заполнения дерева.
  • Офлайн означает отсутствие внешних сервисов при запуске и проверках после bootstrap. Первичная установка зависимостей требует registry либо подготовленного cache; air-gapped bootstrap без артефактов не обещается.

3. Логирование всех этапов — обязательная часть архитектуры

  • Спроектировать диагностику всей будущей цепочки: компоненты и операции, начало, успешное завершение, ошибки, отмена, timeout, восстановление, очереди и отбрасывание поздних событий.
  • Структурированные события содержат идентификатор события/операции, компонент, исход, длительность там, где применимо, и безопасную причину ошибки. Корреляция по session_id; turn_id/generation_id/task_id/trace IDs добавляются по мере появления соответствующих сущностей, без фиктивных IDs в каркасе.
  • Единые уровни и настраиваемая подробность по компонентам. Локальная диагностика работает независимо от LangSmith.
  • Разделить назначения диагностических логов, SQLite state и LangSmith traces, не копировать бессмысленно полный payload во все три. Наличие локальных логов не заменяет обязательный H7 первого голосового этапа.
  • Credentials исключаются до записи; безопасная политика полей/ошибок и контролируемое включение чувствительного содержимого. Никаких дампов auth configs, environment или реальных секретов для проверки.
  • Ограниченный объём, явная политика хранения/ротации, поведение при ошибке записи. Не логировать каждый аудиосэмпл или токен по умолчанию. Не строить собственную платформу observability.
  • В текущем каркасе реализовать диагностику всех существующих этапов session-пути, startup/shutdown и ошибок. Для будущих компонентов зафиксировать карту событий и точек диагностики, не писать неиспользуемые реализации.

4. Архитектура тестов без разрастания

  • Матрица «риск → самый дешёвый достаточный уровень»: unit, contract, integration, немного реальных browser/hardware проверок по мере появления функций. Один и тот же сценарий не дублируется механически на каждом уровне.
  • Проверки устойчивого публичного поведения, а не выбранных классов, порядка всех внутренних вызовов или точного текста каждой строки лога.
  • Небольшая начальная suite с настоящим временным SQLite: session create/snapshot/restart/resume, repeat resume, malformed/unknown/unsupported input, startup/shutdown и недоступный DB path. Не подставлять незаметно memory store при ошибке диска.
  • Проверки логирования защищают диагностически полезные поля/корреляцию и отсутствие синтетического секретного маркера, а не snapshot всей трассы с нестабильными IDs.
  • Никакого обязательного процента coverage, квоты тестов или искусственного отношения test/production LOC. Каждый тест должен назвать предотвращаемую ошибку. Реальное дублирование устранять при ревью.
  • Минимум fixtures, mocks и helpers; выделять их по реальному повторению. Не создавать fake STT/LLM/TTS/Pi, тесты пустых интерфейсов или собственный test framework. Не использовать sleep для синхронизации, если нужны управляемые события.
  • Обычная suite не требует GPU, моделей, credentials и внешних сервисов. Hardware/API experiments запускаются отдельно и отсутствие оборудования не выдаётся за успешную проверку.
  • Один документированный набор format/lint/type/tests для реально существующего кода. Проверки должны использовать настоящую инициализацию/освобождение приложения, не обходить lifespan.

Исследованные основания

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

Не входит

Полноценный голос и synthetic turn; внешние provider/agent adapters; загрузка моделей и inference/Pi эксперименты; установка или покупка LangSmith; замена согласованного продукта; прохождение H1/H2/H7; ремонт v2 и посторонних служб; реализация VOCA-4–12/1; merge PR. LangSmith entitlement/image access, размещение и ресурсные ограничения остаются блокерами будущего голосового этапа, но не этого явно офлайн-фундамента.

Критерии готовности

  • При чтении архитектуры видны модули, разрешённое направление зависимостей, владельцы ресурсов и путь реализации каждого тикета очереди. Реализованное отделено от будущего.
  • После документированного bootstrap приложение и диагностическая панель запускаются; проверки воспроизводимы, не используют реальные модели/GPU/секреты/внешние сервисы.
  • Пользователь создаёт сессию в панели, получает ID/snapshot, перезапускает backend на том же SQLite и восстанавливает ту же session. Проверен именно browser-вход, не только API.
  • Repeat resume не создаёт вторую session и не включает разговор; невалидные команды и неизвестный ID дают понятные ошибки без ложной готовности голоса.
  • Startup/shutdown действительно открывают/освобождают ресурсы; недоступная БД не подменяется скрытым memory store.
  • По логам одного сценария можно проследить его этапы, корреляцию и найти компонент/причину сбоя без добавления временных print. Подробность регулируется по компонентам, политика хранения/объёма проверена, credentials не записываются.
  • Тестовая стратегия привязывает проверки к рискам всей очереди; начальная suite защищает только существующее полезное поведение и не дублирует его на каждом уровне. Есть небольшие проверки диагностики/секретов без привязки к точному тексту логов.
  • PR содержит документы, минимальный исполняемый каркас и основу тестов. Независимое ревью дополнительно проверило лишние абстракции, связанность, диагностические пробелы, ненужные fixtures и дублирование тестов; замечания исправлены.
  • Инженеру предоставлены PR, команды запуска/проверок и сценарий приёмки. Задача доведена через /do и сабагентов до review; merge не выполняется.

Согласование

Инженер выбрал реальный persisted-session каркас без synthetic turn, React + TypeScript + Vite, pnpm; отдельно потребовал логирование всех этапов на уровне архитектуры. Полный объём подтверждён словами «Да, заводи задачу в полном обьеме». Ранее явно разрешён запуск нужного flow с /do и сабагентами до готового PR на его проверку.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions