добавлен конвейер ревью и пайплайн задачи
- одиннадцать проходов ревью перенесены из jellybit и переписаны под домен: приём пакетов, слои, координатная идентичность, чувствительность данных - скиллы task-pipeline и review-pipeline, контракт находок, журнал промахов
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
# Контракт находок
|
||||
|
||||
Единый формат для всех проходов конвейера ревью. Проход, нарушивший контракт,
|
||||
считается сломанным — триаж вправе выбросить его вывод целиком.
|
||||
|
||||
## Форма находки
|
||||
|
||||
```
|
||||
### <краткая формулировка ПОСЛЕДСТВИЯ, не симптома>
|
||||
- Файл: internal/store/bucket.go:120-134
|
||||
- Severity: critical | major | minor | nit
|
||||
- Confidence: high | medium | low
|
||||
- Оракул: <падающий тест / команда с выводом / положение гайда / нет>
|
||||
- Последствие: <что произойдёт и при каких условиях>
|
||||
- Предложение: <конкретное изменение>
|
||||
- Найдено проходом: <имя агента>
|
||||
```
|
||||
|
||||
## Правила
|
||||
|
||||
- **Заголовок через последствие.** Не «нет проверки токена», а «читатель без
|
||||
токена выгрузит всю историю пульса». Не «слияние перезаписывает точку», а
|
||||
«повторная доставка сотрёт `start`/`end` у уже сохранённой точки, и восстановить
|
||||
их можно только из экспорта Apple». Симптом в
|
||||
заголовке — это заявка на то, что читатель сам достроит последствие; он не
|
||||
достроит, он просто починит симптом.
|
||||
- **`critical` без оракула или построенного пути не существует.** Оракул — это
|
||||
падающий тест, вывод выполненной команды или поимённое положение гайда. Не
|
||||
«вероятно, здесь гонка», а `CGO_ENABLED=1 go test -race` с выводом детектора.
|
||||
- **`confidence: low` — это «так обычно пишут».** Такие находки допустимы, но не
|
||||
поднимаются выше `minor`. Частотность конструкции в публичном Go — не аргумент.
|
||||
- **Находка без поля «Последствие» не выводится вовсе.** Пустое «Последствие:
|
||||
ухудшает читаемость» равносильно отсутствию поля.
|
||||
- **`nit` допустим только при нарушении записанной конвенции** — со ссылкой на
|
||||
файл и раздел `docs/conventions.md` либо на правило `.golangci.yml`. Если
|
||||
правило механизируемо, но не механизировано — это не находка ревью, это
|
||||
`Promote candidate` (см. [promote.md](promote.md)).
|
||||
- **Расхождение — не дефект, пока не названо последствие.** Особенно для
|
||||
`healthlog-review-reimpl`: «я бы сделал иначе» без последствия не выводится.
|
||||
|
||||
## Шкала severity
|
||||
|
||||
| Severity | Что это | Пример |
|
||||
|---|---|---|
|
||||
| `critical` | нарушение инварианта безопасности данных, потеря/порча данных, утечка секрета, построенный путь к отказу | точка потеряна при слиянии часового объекта, тело выгрузки Apple Health в поле лога |
|
||||
| `major` | сломанное требование дельта-спеки, необрабатываемый отказ штатного сценария, флаки-тест, поведение вне спеки, меняющее исход | приём отвечает 200, не записав тело в архив: доставка считается принятой, а данных нет |
|
||||
| `minor` | отступление от конвенции с реальной ценой, отсутствующая наблюдаемость, дублирование, которое разойдётся | разбор пакета не пишет ни одного чекпоинта, и молчащая автоматизация неотличима от пустого потока |
|
||||
| `nit` | нарушение записанной конвенции без последствий за пределами чтения | `msg` с интерполяцией вместо константы |
|
||||
|
||||
## Блок границ покрытия
|
||||
|
||||
Каждый проход завершает вывод этим блоком. Он не сокращается и не заменяется
|
||||
фразой «всё проверено».
|
||||
|
||||
```
|
||||
## Coverage of this pass
|
||||
- проверено: <что реально прочитано/запущено, с путями и командами>
|
||||
- не проверялось и почему: <бюджет, недоступный инструмент, вне входа>
|
||||
- принципиально недоступно этому проходу: <из charter'а агента>
|
||||
```
|
||||
|
||||
## Финальный отчёт триажа
|
||||
|
||||
Секции строго в этом порядке, потолок — 7 пунктов в первых двух:
|
||||
|
||||
1. `Блокирует мердж` (≤3, каждая с оракулом);
|
||||
2. `Стоит исправить сейчас` (≤4);
|
||||
3. `Гипотезы без доказательства` — что понижено и почему;
|
||||
4. `Promote candidates` — кандидаты в конвенцию или правило линтера;
|
||||
5. `Границы покрытия` — сводная, обязательная.
|
||||
|
||||
Каждая находка в секциях 1–2 несёт дополнительное поле:
|
||||
|
||||
```
|
||||
- Действие: инлайн | развилка
|
||||
```
|
||||
|
||||
`инлайн` — оркестратор чинит сам, не спрашивая и не логируя. `развилка` — цена
|
||||
исправления сопоставима с переработкой, либо выбор меняет scope, либо решение
|
||||
трогает инвариант: идёт человеку через `AskUserQuestion` вопросом с вариантами.
|
||||
|
||||
Потребитель отчёта — оркестратор, который **реализует прочитанное**. Поэтому
|
||||
потолок в 7 пунктов — не забота о внимании читателя, а защита кодовой базы от
|
||||
правок, которых никто не заказывал.
|
||||
@@ -0,0 +1,89 @@
|
||||
# Промоут: находка → конвенция → правило → удаление
|
||||
|
||||
Механизм храповика. Без него конвейер выдаёт одни и те же находки бесконечно, а
|
||||
конвенции не растут — то есть внимание тратится повторно на уже решённое.
|
||||
|
||||
Роли уровней:
|
||||
|
||||
- **generative-проходы** — механизм *открытия* неявного (дорого, шумно, но
|
||||
только они достают то, чего нет в списках);
|
||||
- **конвенции** — дешёвая *регрессионная сетка* на уже открытое;
|
||||
- **правила линтера** — то же с детерминированным оракулом и нулевой ценой
|
||||
внимания.
|
||||
|
||||
## Шаг 1. Находка → конвенция
|
||||
|
||||
Условия: находка **принята** при ревью (не отвергнута, не понижена в гипотезу) и
|
||||
**не специфична для одного места**.
|
||||
|
||||
- Формулируется как **проверяемое свойство**, а не как совет: «уровень доменного
|
||||
отказа выбирает единственный логирующий чокпоинт», а не «внимательнее с
|
||||
уровнями логов».
|
||||
- Записывается источник — какой проход нашёл. Это единственные данные для
|
||||
калибровки: проход, чьи находки регулярно доезжают до конвенции, оправдан;
|
||||
проход, чьи находки не доезжают никогда, — кандидат на `drop`.
|
||||
- Место записи — соответствующий файл `docs/conventions.md`. Если тема
|
||||
относится к поведению системы, а не к тому, как мы пишем код, — это не
|
||||
конвенция, а требование: заводится дельта-спека OpenSpec обычным путём.
|
||||
|
||||
Промоут идёт **тем же путём, что change → spec**: правка попадает в тот же
|
||||
коммит, что и исправление кода, с пометкой в сообщении — история промоутов
|
||||
видна в `git log docs/conventions/`.
|
||||
|
||||
## Шаг 2. Конвенция → правило
|
||||
|
||||
Как только свойство выражается детерминированно, оно переезжает в инструмент.
|
||||
Порядок предпочтения — от дешёвого к дорогому:
|
||||
|
||||
1. **готовый линтер** в `.golangci.yml` (`sloglint`, `errorlint`, `depguard`,
|
||||
`forbidigo`, `misspell`, стандартный набор v2);
|
||||
2. **`forbidigo`/`depguard` с собственным паттерном** — запрет идентификатора или
|
||||
импорта;
|
||||
3. **`revive`/`gocritic` с настройкой** — когда нужна форма, а не имя;
|
||||
4. **тест-сканер исходников** `internal/arch_test.go` — когда правило про
|
||||
структуру проекта или SQL: направление зависимостей, `AUTOINCREMENT` в
|
||||
миграциях, матчинг ошибки по тексту, бизнес-логика в транспорте;
|
||||
5. **`go/analysis`-анализатор** — последний рубеж, заводим только если 1–4 не
|
||||
выражают правило.
|
||||
|
||||
Правило обязано быть **зелёным на текущем коде в момент включения**: иначе
|
||||
lefthook блокирует любой коммит, и правило снимут первым же раздражённым
|
||||
движением. Приводить код в соответствие — часть шага 2, отдельным коммитом.
|
||||
|
||||
## Шаг 3. Удаление из конвенций и из промптов
|
||||
|
||||
**Шаг, который пропускают чаще всего, и единственный, ради которого затевались
|
||||
первые два.**
|
||||
|
||||
Как только правило работает:
|
||||
|
||||
- из `docs/conventions.md` убирается формулировка правила; остаётся, если
|
||||
нужно, одна строка «проверяется линтером `<имя>`» — но только там, где без неё
|
||||
раздел теряет связность;
|
||||
- из charter'ов агентов (`.claude/agents/healthlog-review-*.md`) убирается
|
||||
соответствующий пункт;
|
||||
- из `openspec/config.yaml` → `context` убирается дубль, если он там был.
|
||||
|
||||
Практический критерий: **в прозаических конвенциях остаётся только то, что
|
||||
принципиально не выражается правилом.** Файл конвенций на несколько сотен строк
|
||||
размазывает внимание модели по тривиальному — она добросовестно проверит
|
||||
именование полей лога и не дойдёт до формы решения. Каждая строка конвенций,
|
||||
которую можно было бы проверить машиной, оплачивается непойманным дефектом
|
||||
где-то ещё.
|
||||
|
||||
## Обратное движение
|
||||
|
||||
Правило, которое даёт ложные срабатывания чаще, чем ловит (порядка трети от
|
||||
общего числа), снимается и возвращается в прозу — или удаляется совсем, если
|
||||
свойство перестало быть важным. Снятие фиксируется там же, где включалось, с
|
||||
одной строкой «почему».
|
||||
|
||||
## Что промоуту не подлежит
|
||||
|
||||
- Находка, специфичная для одного места (её лечит комментарий в коде).
|
||||
- Вкусовщина: не меняет поведения, не влияет на стоимость следующего изменения,
|
||||
не нарушает записанного. Такое выбрасывается на триаже и не хранится.
|
||||
- Свойство, требующее знания рантайма (профиль нагрузки, история инцидентов) —
|
||||
его нельзя проверить ни промптом, ни линтером; место такому — в
|
||||
[journal.md](../../../../docs/review/journal.md) как «признано
|
||||
неавтоматизируемым».
|
||||
Reference in New Issue
Block a user