httpapi: форма провода читающих маршрутов объявлена транспортом

- доменные типы internal/catalog лишились json-тегов и MarshalJSON; типы
  metricWire/layerWire/aggregationWire и перевод catalogWire живут в транспорте,
  тело отказа тоже получило объявленный тип — байты ответа не изменились
- заведён сторож: обход графа типов ответа утверждает, что домен не доезжает до
  сериализации, плюс требование json-тега на полях транспортных структур и
  заведомо красные случаи к обоим правилам
- решение с ценой обеих сторон записано в architecture.md и ADR; шаг lint в
  гейте получил свой кеш — общий на машину красил прогон находками из чужого
  worktree
This commit is contained in:
av
2026-08-04 16:18:05 +03:00
parent bd832337df
commit a834d10415
19 changed files with 1721 additions and 53 deletions
+38
View File
@@ -552,3 +552,41 @@
описывать тот случай, ради которого снято» действует только для тестов
(`conventions/testing.md`). На `design.md` оно теперь распространено записью
ниже, но механизировать его нечем.
## 2026-08-04 — гейт дважды покраснел от чужого мусора: кеш линтера и черновик в `./tmp` [пойман]
- **Где:** конвейер, а не код — `scripts/gate.py`, шаги `lint` и `test`
- **Симптом:** в задаче про форму провода `task gate` дал `FAIL lint` с
сообщением `../../internal/store/store.go:260: use of time.Now forbidden` —
путь ведёт в **главный репозиторий**, а прогон шёл в worktree задачи. Позже, в
том же прогоне задачи, `FAIL test` на
`TestОднаМеткаИзТелаУбиваетМаршрутКаталога` — тесте, которого в задаче нет
вовсе.
- **Причина:** два разных механизма, один класс — в гейт затекает то, что к
изменению отношения не имеет.
- `golangci-lint` ходит в **общий на машину** `~/.cache/golangci-lint`, а
конвейер задач работает в нескольких worktree одного модуля (`tmp/wt-*`).
Кеш отдаёт замечания, привязанные к путям чужого дерева, и правило-исключение
`^internal/(ident|store)/` на путь вида `../…` не распространяется.
- `go test ./...` не знает про `./tmp`: `.golangci.yml` каталог исключает
(коммит `9f77e56`), а `go test` — нет. Проход `adversary` оставил там свой
падающий тест-оракул, и он стал частью набора.
- **Чем воспроизведён:** первое — независимо проходом `review-gate`: временный
worktree базовой ревизии, `golangci-lint run ./...` без очистки кеша даёт
замечание с путём **другого** дерева; после `golangci-lint cache clean` на той
же ревизии — `0 issues`. Второе — `tmp/gate/test.log`: `FAIL` в пакете
`git.vakhrushev.me/av/healthlog/tmp/adv/oracle`.
- **Чем пойман:** обоими случаями — самим гейтом, но **ценой разбора**: краснота
выглядела как дефект изменения, и каждый раз пришлось доказывать, что это не
он. Ровно та цена, что названа записью 2026-08-04 выше: «краснота по причине,
не связанной с изменением, приучает не читать красноту».
- **Что изменено:** шаг `lint` получил свой кеш —
`GOLANGCI_LINT_CACHE=tmp/gate/golangci`, — то есть прогон стал герметичным по
дереву. Цена названа и замерена проходом `ops`: N деревьев × 10–15 МиБ вместо
одного общего кеша, штатный трим go-build-формата у него есть.
- **Что осталось незакрытым:** `go test ./...` по-прежнему видит черновые
go-пакеты в `./tmp`. Убирать за собой обязан тот, кто их создал (в этот раз —
проход ревью), и механизма против забывчивости нет. Дешёвый кандидат, если
класс повторится: `go test` по явному списку `./cmd/... ./internal/...` вместо
`./...`. Не сделано намеренно — один случай не отличим от случайности, а
правило, введённое по одному случаю, потом никто не помнит зачем.