# ✨ Собрать каркас приложения и раздать его из бинарника - **Тип:** feature - **Категория:** Очередь - **Зачем:** Экранов нет и собирать их нечем: ни сборки фронтенда, ни раздачи статики в проекте не существует. - **Теги:** goal:web-access Ни одной строки «Завершения» цели каркас не закрывает: он готовит четыре экранные — 1, 2, 3 и 4, — но сам по себе пользователю ничего не даёт. Видимое от него одно: приложение открывается и достаёт данные с живого сервера, а не отдаёт пустую страницу. Фреймворк выбран 2026-08-11 — Vue 3 с роутером пятой версии и сборкой Vite ([ADR](../../docs/adr/ADR-2026-08-11-spa-on-vue.md)); правила кода приложения лежат в [conventions/web-ui.md](../../docs/conventions/web-ui.md). ## Затрагивает - новый каталог фронтенда: исходники, зависимости, конфигурация сборки; - роут, отдающий `index.html` на неизвестный путь вне `/api/`: адреса маршрутов обычные, а не после решётки; - `Taskfile.yml` — шаг сборки статики и его место в `task gate` и `task image`; - `Dockerfile` — сборка статики внутри образа, чтобы выкладка не зависела от машины разработчика; - раздача статики из бинарника через `go:embed` и роут под неё; - `.gitignore` — каталог зависимостей и собранная статика; - `docs/conventions/web-ui.md` — переписывается под выбранный фреймворк; - `docs/architecture.md` — новый компонент и новая внешняя зависимость сборки. ## Критерии приёмки - Статика собирается одной командой и вшивается в бинарник: запущенный бинарник отдаёт приложение без каталога рядом. Оракул — сборка, запуск бинарника из пустого каталога, запрос корня возвращает разметку приложения. - Образ собирается на чистой машине без предустановленного окружения фронтенда. Оракул — `task image` без локально установленных зависимостей фронтенда. - Приложение показывает, кто вошёл, и обращается к API живого сервера. Оракул — тест либо ручная проверка на запущенном сервере с сессией. - Шаг сборки статики входит в `task gate` и краснеет при ошибке сборки. Оракул — намеренно сломанный исходник роняет `task gate`. - Обновление страницы на любом маршруте приложения открывает тот же экран, а адрес внутри `/api/` в приложение не проваливается. Оракул — тест на два запроса: неизвестный путь вне `/api/` отдаёт разметку, неизвестный путь внутри `/api/` отдаёт ошибку контракта. ## Рамки Экранов, кроме заглушки с именем вошедшего, не делаем — это `upload-and-status-screen` и `records-list-screen`. Установку на телефон не делаем — это `installable-pwa`. Готовый набор компонентов не берём: он не выбран, а способен удвоить собранный файл. Шаг сборки входит в гейт и в образ решением [ADR](../../docs/adr/ADR-2026-08-11-spa-on-vue.md) 2026-08-11 — заново это решение не принимается. Во что слой Node обходится образу по времени и по весу, не замерялось: замер делает эта задача.