приложение собрано каркасом и вшито в бинарник

- заведён каталог web/ — Vue 3, роутер пятой версии, сборка Vite; собранное
  вшивается через go:embed и раздаётся корневым маршрутом: разметка на
  неизвестном пути вне корней сервиса, отказ контракта внутри корня
- перечень корней сервиса стал единой точкой и порождает регистрацию маршрутов,
  а не описывает её; журнал раздачи пишет исход и длину пути, но не сам путь
- шаг front зовёт Node контейнером docker — Biome, юнит-тесты Vue и сборка
  входят в гейт, а в Dockerfile появилась ступень приложения
This commit is contained in:
av
2026-08-15 18:51:05 +03:00
parent c6ffda9aac
commit 663021f712
46 changed files with 5958 additions and 43 deletions
+3
View File
@@ -130,6 +130,9 @@
| Скрипты оболочки | `Taskfile.yml` → шаг `shell` (`shellcheck`), он же на pre-commit |
| Форма `Dockerfile` | `Taskfile.yml` → шаг `dockerfile` (`hadolint`), он же на pre-commit |
| Одно число версии Go в `go.mod`, `Dockerfile`, `CLAUDE.md` и `README.md` | `Taskfile.yml` → шаг `go-version` (`scripts/check-go-version.sh`) |
| Форматирование и статический анализ кода приложения | `web/biome.json` → Biome, зовётся шагом `front` командой `npm run check`. Разбирает и однофайловые компоненты; правила — набор `recommended` плюс своя форма (одинарные кавычки, точка с запятой по необходимости) |
| Типы разметки и кода приложения | `vue-tsc`, и он входит в **команду сборки**, а не стоит отдельным шагом: несобираемое приложение и непроверенные типы — один отказ |
| Поведение экранов приложения | `Taskfile.yml` → шаг `front`, юнит-тесты Vue (`npm run test`). Без них требование «приложение показывает вошедшего» не проверял бы никто, а набор проверок оставался бы зелёным на сломанном экране |
### Хранилище, документы, секреты, зависимости
+3 -1
View File
@@ -94,7 +94,7 @@ OpenSpec.
- Доменные поля — плоский `snake_case`.
- Системные домены — точечная иерархия (по образцу OpenTelemetry): `http.*`,
`ext.*`.
`ext.*`, `webapp.*`.
- JSON плоский: все поля на верхнем уровне, без вложенности.
| Когда добавляем | Поля |
@@ -103,6 +103,8 @@ OpenSpec.
| на задачу | `capability` (значения — по именам заведённых capability в `openspec/specs/`), `record_id`, `file_id`, `source` |
| на запись об ошибке | `error` |
| на вызов внешнего сервиса | `ext.service`, `ext.operation`, `ext.status_code`, `duration_ms`, `retry` |
| на запрос, отданный приложению | `webapp.outcome` (`markup`, `asset`, `failure` — перечень закрыт), `http.path_length`. Самого пути в строке нет: его выбирает спрашивающий, и дословная запись сделала бы журнал местом, куда аноним пишет свой текст. Вместо пути в `http.route` стоит `<приложение>` |
| на подъёме сервиса | `webapp.build` — отпечаток вшитой сборки; им «не та сборка» отличается от «той» |
Не заводим `service.*` и `host.*` — для одного бинарника на одном хосте это шум.
+31 -8
View File
@@ -10,17 +10,19 @@
описывала htmx с прямым запретом на шаг сборки и реактивные фреймворки; она снята
целиком вместе со сменой решения на SPA 2026-08-10.
**Кода приложения ещё нет.** Правила ниже выведены из выбора и из замера на
пробном экране, а не из написанного кода: первым их применяет и проверяет
`spa-skeleton`. Место, где правило разойдётся с тем, что окажется удобным, —
повод править эту запись, а не обходить её молча.
Правила ниже применены каркасом приложения (`spa-skeleton`, 2026-08-15): до него
они были выведены из выбора и из замера на пробном экране, а не из написанного
кода. Место, где правило разойдётся с тем, что окажется удобным, — повод править
эту запись, а не обходить её молча.
Логирование запросов — [logging.md](logging.md). Трансляция доменных ошибок
наружу — [errors.md](errors.md).
**Механизировано:** типы разметки и кода проверяет `vue-tsc`, и он входит в
команду сборки, а не стоит отдельным шагом. Правил линтера для кода приложения
пока нет.
команду сборки, а не стоит отдельным шагом. Форматирование и статический анализ
держит **Biome**, поведение экранов — **юнит-тесты Vue**; оба шага входят в набор
проверок наравне со сборкой. Инструменты зовутся контейнером, а не из `PATH`:
требованием к машине разработчика остаётся docker, а не установленный Node.
## Что решено про само приложение
@@ -51,7 +53,14 @@
делать то же самое.
- **Собранная статика неизменяема и адресуется хешем в имени.** Имена придумывает
Vite, руками их не задаём: от этого зависит обновление установленного
приложения.
приложения. Сервис на это правило опирается, но проверить его не может — имён
он не выбирает, — поэтому дом правила здесь, а не в спеке: долгий срок
хранения он ставит **по каталогу** сборщика, и файл, положенный туда без
отпечатка в имени, останется в хранилище браузера навсегда.
- **Зависимости ставятся из файла замка командой, которая его не правит.** Иначе
набор проверок пачкает рабочее дерево, обновление зависимости приезжает в
коммит без чьего-либо решения, а собранное в наборе проверок перестаёт
совпадать с собранным в образе.
- **Шаг сборки входит в `task gate` и в сборку образа.** Красная сборка статики
роняет гейт наравне с `go build`.
@@ -70,7 +79,14 @@
`/_/` у панели, — плюс `/health` и `/metrics` отдельными адресами. Приложение
уехало из общего `/api/` решением владельца 2026-08-15: пространство
принадлежит хранилищу, и обновление библиотеки вправе занять там имя рядом с
нашим.
нашим. Перечень корней сервису не описывают, а из него **порождают**
регистрацию маршрутов: описанный порознь, он разошёлся бы с ними молча.
- **Несовпавший ресурс разметкой не подменяется.** Путь под каталогом сборщика,
которому не нашлось файла, отвечает `404`. Правило — вторая половина
предыдущего: разметка прежней сборки называет ресурсы прежней сборки, и
подменить их разметкой значит ответить `200` на то, чего нет. Браузер отвергнет
такой ответ по типу содержимого, человек увидит пустой экран, а в кодах
ответов сервиса не останется ничего.
- **Экран не знает, как он открыт.** Данные экран берёт по своему адресу, а не
получает от предыдущего: приложение открывают по ссылке и обновляют страницу
посередине.
@@ -107,6 +123,13 @@
## Что не решено
- **Инструмент статического анализа проверен наполовину.** Biome взят решением
владельца 2026-08-15 и разбирает однофайловые компоненты; замены он потребует,
если перестанет их держать. Тогда это отдельное решение, а не подстановка по
ходу.
- **Проверка типов держится на пятой линии TypeScript.** С седьмой `vue-tsc`
не работает: новый компилятор не отдаёт точку входа, которую тот зовёт.
Проверено прогоном 2026-08-15.
- **Набор компонентов и стили.** Своя разметка или готовый набор — не решено, а
готовый способен удвоить собранный файл
([research/spa-framework.md](../research/spa-framework.md), «Чего разведка не