Files
transcriber/openspec/changes/archive/2026-08-15-spa-skeleton/tasks.md
T
av 663021f712 приложение собрано каркасом и вшито в бинарник
- заведён каталог web/ — Vue 3, роутер пятой версии, сборка Vite; собранное
  вшивается через go:embed и раздаётся корневым маршрутом: разметка на
  неизвестном пути вне корней сервиса, отказ контракта внутри корня
- перечень корней сервиса стал единой точкой и порождает регистрацию маршрутов,
  а не описывает её; журнал раздачи пишет исход и длину пути, но не сам путь
- шаг front зовёт Node контейнером docker — Biome, юнит-тесты Vue и сборка
  входят в гейт, а в Dockerfile появилась ступень приложения
2026-08-15 18:51:05 +03:00

148 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## 1. Каталог приложения и его сборка
- [x] 1.1 Завести `web/` с `package.json`, файлом замка, настройкой Vite и
`index.html`; выходной каталог сборщика — `web/embed/dist`
- [x] 1.2 Команда сборки приложения гоняет проверку типов и сборку одним вызовом
(`vue-tsc` входит в команду сборки, а не стоит отдельным шагом)
- [x] 1.3 В git лежит `web/embed/.gitkeep` — этажом выше выходного каталога, чтобы
очистка перед сборкой его не уносила; собранное и каталог зависимостей внесены
в `.gitignore`
- [x] 1.4 Написан экран-заглушка: показывает имя вошедшего, на `401` ведёт ко
входу, на прочий отказ показывает строку о неудаче; одна таблица маршрутов
через `createRouter`, адреса обычные (`createWebHistory`)
- [x] 1.5 Обращение к API идёт через одну свою обёртку над `fetch`; она же
единственное место, где читается код ответа
- [x] 1.6 Заведён Biome: форматирование и статический анализ кода приложения.
Не покрывает разметку однофайловых компонентов — сказать строкой и остановиться,
замена инструмента решается человеком
- [x] 1.7 Заведены юнит-тесты Vue: показ вошедшего, увод ко входу на `401`,
строка о неудаче на прочем отказе, учётная запись без имени
## 2. Раздача приложения из бинарника
- [x] 2.1 `web/embed.go` вшивает `web/embed` целиком (`all:`), включая метку
пустого каталога
- [x] 2.2 Перечень корней сервиса заведён единой точкой в пакете транспорта:
`/api`, `/app`, `/auth`, `/_` и отдельные адреса `/health`, `/metrics`
- [x] 2.2.1 Наши маршруты вешаются **из перечня**: проба здоровья, метрики,
группа приложения, группа входа. Корни библиотеки остаются её литералами и
держатся своим тестом; второе перечисление адресов наблюдения в уровне журнала
снято
- [x] 2.3 Принадлежность корню — точное совпадение либо префикс вместе с косой
чертой; оба условия проверяются
- [x] 2.4 Обработчик приложения зарегистрирован маршрутом `/` (тем самым занимает
место библиотечного отказа) и принимает файловую систему параметром
- [x] 2.5 Порядок обработчика: корень сервиса или адрес наблюдения — отказ формой
библиотеки; метод не `GET` и не `HEAD``405`; несовпавший путь под каталогом
ресурсов — `404`; совпавший файл — файл; прочее — разметка
- [x] 2.6 Заголовки кэширования назначены по каталогу: ресурсы сборщика — год и
«неизменяемо», всё прочее, включая разметку, — спрашивать заново
- [x] 2.7 Отсутствие собранного приложения даёт `503` с честной страницей и
строку в журнале при подъёме, а не `404` молча; проба здоровья остаётся зелёной
- [x] 2.8 При подъёме в журнал уходит отпечаток вшитой разметки
- [x] 2.9 Строка журнала корневого маршрута несёт исход и длину пути, но не сам
путь
- [x] 2.10 Разметка и ресурсы отдаются без сессии; адреса данных под корнем
приложения сессии по-прежнему требуют
## 3. Сборка и выкладка
- [x] 3.1 Шаг сборки приложения заведён в `Taskfile.yml` и стоит **первым** в
наборе проверок, до сборки Go; рядом — шаги Biome и юнит-тестов приложения
- [x] 3.1.1 Проверки приложения гоняются тем же контейнером, что и сборка;
второго окружения не заводится
- [x] 3.2 Шаг зовёт установщик и сборщик контейнером, а не из `PATH`: имя образа
берётся из `Dockerfile`, контейнер ходит под вызвавшим пользователем, кэш
установщика уведён в каталог под `/tmp`
- [x] 3.3 Зависимости ставятся из файла замка командой, которая его не правит; ту
же команду зовёт ступень `Dockerfile`
- [x] 3.4 Недостающий docker и отказ сети или реестра — отказ окружения, код 3;
красная сборка приложения — код 1
- [x] 3.5 В `Dockerfile` добавлена ступень сборки приложения перед сборкой
бинарника; в рабочий слой Node не попадает
- [x] 3.6 Вес выяснен, время замера снято с задачи решением владельца
2026-08-15: финальный образ от ступени приложения не растёт вовсе (Node в него
не копируется), вшитое добавляет к бинарнику 86 072 байта. Время сборки
остаётся неизвестным сознательно
- [x] 3.7 Последствие ADR-2026-08-11-spa-on-vue поправлено: требованием к машине
разработчика становится docker, а не Node
## 4. Проверки
- [x] 4.1 Тест: неизвестный путь вне корней отдаёт разметку с кодом `200`
- [x] 4.2 Тест: неизвестный путь под каждым корнем сервиса разметки не отдаёт
- [x] 4.3 Тест: голый корень без косой черты разметки не отдаёт, а посторонний
путь, начинающийся именем корня без косой черты, — отдаёт
- [x] 4.4 Тест: неизвестный путь под корнем приложения отвечает `404` с сессией и
`401` без неё
- [x] 4.5 Тест: несуществующий файл под каталогом ресурсов отвечает `404`
- [x] 4.6 Тест: проба здоровья и метрики приложением не подменяются
- [x] 4.7 Тест: чужой метод отвечает `405`, а `HEAD` на разметку — `200`
- [x] 4.8 Тест: разметка и ресурс отдаются без сессии, а список записей — нет
- [x] 4.9 Тест: у разметки стоит требование спрашивать заново, у ресурса из
каталога сборщика — год и «неизменяемо»
- [x] 4.10 Тест: пустая сборка даёт `503`, а не `404`, и проба здоровья при этом
зелёная
- [x] 4.11 Юнит-тесты приложения краснеют на сломанном экране: намеренная поломка
показа вошедшего роняет набор проверок
- [x] 4.12 `task gate` зелёный целиком
## 5. Документы
- [x] 5.1 `docs/conventions/web-ui.md` — снята оговорка «кода приложения ещё
нет»; дописаны правила: отпечаток в именах ресурсов, несовпавший ресурс отвечает
`404`, зависимости ставятся из файла замка
- [x] 5.2 `docs/architecture.md` — компонент приложения, внешняя зависимость
сборки, новая capability и перечень корней в единых точках
- [x] 5.3 `docs/database.md` — срок хранения ресурсов в настройках с числовым
значением
- [x] 5.4 `CLAUDE.md` — команды и требования к машине разработчика, состав гейта
вместе с проверками приложения, второй сетезависимый шаг названный поимённо
- [x] 5.5 `docs/review.md` — заведён род узла «раздача собранной статики и шаг
сборки приложения» в типовых узлах
- [x] 5.6 `docs/conventions/go-linters.md` либо соседняя запись — чем проверяется
код приложения: Biome и юнит-тесты, что каждый ловит и где настроен
## Критерии приёмки
Дословно из записи задачи `spa-skeleton`:
- Статика собирается одной командой и вшивается в бинарник: запущенный бинарник
отдаёт приложение без каталога рядом. Оракул — сборка, запуск бинарника из
пустого каталога, запрос корня возвращает разметку приложения.
- Образ собирается на чистой машине без предустановленного окружения фронтенда.
Оракул — `task image` без локально установленных зависимостей фронтенда.
- Приложение показывает, кто вошёл, и обращается к API живого сервера. Оракул —
тест либо ручная проверка на запущенном сервере с сессией.
- Шаг сборки статики входит в `task gate` и краснеет при ошибке сборки. Оракул —
намеренно сломанный исходник роняет `task gate`.
- Обновление страницы на любом маршруте приложения открывает тот же экран, а
адрес внутри корня сервиса в приложение не проваливается. Оракул — тест на три
запроса: неизвестный путь вне корней отдаёт разметку, неизвестный путь внутри
`/api/` и внутри `/app/` отдаёт ошибку контракта.
Рубрика ревью дизайна, тем же списком:
- Перечень корней живёт одной точкой, и добавление корня не требует правки в двух
местах.
- Запрос несуществующего файла под каталогом ресурсов отвечает `404`, а не
разметкой с `200`.
- После прогона сборки приложения `git status` чист по `web/`.
- После `task gate` `git status` чист по файлу замка зависимостей.
- Отсутствие сборки громкое: код ответа, страница и строка журнала; «сборки нет»
отличается от «файла нет».
- Долгий неотзываемый срок ставится только файлу из каталога ресурсов сборщика.
- Разметка и ресурсы отдаются анонимно, данные под корнем приложения — нет; по
анонимному ответу нельзя узнать, вошёл ли кто-то.
- В бинарник попадает только собранное: ни карт исходников, ни каталога
зависимостей, ни файлов вне выходного каталога.
- Отпечаток вшитой сборки виден строкой журнала при подъёме.
- Шаг сборки следует словарю кодов: недостающий инструмент и отказ сети — 3,
красная сборка — 1; молчаливого пропуска нет.
- Ступень сборки не протекает в рабочий слой образа, а версия сборочного
окружения объявлена одним местом.
- Файловая система строится один раз, а не на каждый запрос; поведение на `HEAD`
определено.
- Запрос на путь длиной в килобайт не добавляет значения ни метке метрики, ни
строке журнала.