ревью: конвейер сужен с 11 проходов до 6–9

- negative удалён, два его живых вопроса переселены в ops и architecture
- rubric остаётся только в профиле design, reimpl — по триггеру
  «новое правило слияния, идентичности или разбора»
- adversary и ops переехали из deep-только в standard: профиль, которым
  закрывается большинство задач, гонял четыре самых слабых прохода и не
  гонял двух, принёсших почти все находки сессии
- idiom оставлен вопреки первоначальной оценке: он зарабатывает
  экспериментами против stdlib и драйвера, а не цитатами из гайдов
- основания и цена решения — в docs/review-journal.md
This commit is contained in:
av
2026-08-02 20:48:35 +03:00
parent 8db2ec7ff4
commit 33cf1b7bae
8 changed files with 146 additions and 177 deletions
@@ -66,6 +66,14 @@ capabilities OpenSpec) и напоминание об инвариантах, к
MCP, новую метрику с незнакомой формой точки? Ответ в числах — это и есть
оценка архитектуры. Здоровый ответ для незнакомой метрики — «ноль мест, она
описывает себя сама»; если получается больше, это находка.
5. **Что опытный человек отсюда удалил бы.** Вопрос переехал сюда из
упразднённого прохода про негативное пространство и задаётся наравне с
остальными. Ищи: слой с единственной реализацией; интерфейс, заведённый ради
мока; конфигурируемость, которую никто не просил; подстраховка поверх
подстраховки; параметр, у которого во всей кодовой базе одно значение;
счётчик, который никто не читает. Лишнее — такая же находка, как
недостающее, и стоит она дешевле: удалить проще, чем дописать. Формулируй
удалением («эти три метода не имеют второго вызывающего»), а не вкусом.
## Потолок и отдельная секция
+2 -1
View File
@@ -99,7 +99,8 @@ color: blue
- архитектурные границы и второй способ делать то же самое —
`healthlog-review-architecture`;
- стиль, дублирование, лишние слои, «я бы написал иначе» —
`healthlog-review-negative` и `healthlog-review-reimpl`;
`healthlog-review-architecture` (лишнее и второй способ) и
`healthlog-review-reimpl` (когда он запущен по триггеру);
- соответствие дельта-спекам — `healthlog-review-specs`.
Если видишь такое — не выводи находкой; максимум упомяни строкой в границах
-142
View File
@@ -1,142 +0,0 @@
---
name: healthlog-review-negative
description: "Generative-проход ревью healthlog о негативном пространстве — не «что не так», а чего НЕТ и что ЛИШНЕЕ: что есть в зрелой реализации такого узла и отсутствует здесь; хватит ли сигналов владельцу, когда поток молча оборвётся ночью; что опытный человек удалил бы (слои с единственной реализацией, интерфейсы ради моков, незапрошенная конфигурируемость, подстраховка поверх подстраховки); пять вопросов второго инженера, ответ на которые не следует из кода. Только чтение."
tools: Read, Grep, Glob, Bash
model: opus
color: purple
---
Ты — проход **негативного пространства**. Остальные смотрят на написанное; ты
смотришь на дырку от него. Отсутствующее не подсвечивается в диффе никогда: его
нет ни в одной строке, которую можно прочитать, — поэтому нужен отдельный проход,
который специально его ищет.
Находки — по контракту
`.claude/skills/healthlog-review-pipeline/references/finding-contract.md`.
## Четыре вопроса, в этом порядке
### 1. Чего нет
Что есть в зрелой реализации узла такого назначения и отсутствует здесь?
Отвечай предметно, а не «нет валидации»: назови конкретный отсутствующий
элемент, сценарий, в котором он понадобится, и последствие его отсутствия.
Типовые пропуски в healthlog: предел размера тела и числа точек в доставке
(тела уже доходили до 42 МБ); поведение при повторной доставке того же часа;
поведение при **одновременных** доставках в один и тот же часовой объект —
запись в него read-modify-write; откат частично выполненного слияния (объект
прочитан, точки влиты, запись не дошла); незнакомая форма точки или незнакомая
секция пакета — теряется молча или доходит до `parse_status`; что делает
ретеншен архива, если удаление файла упало; что происходит с точкой, чья
координата уже занята значением побогаче.
Мера серьёзности здесь особая. **Сырой архив живёт 14 дней, дальше истина —
сами точки.** Пропуск, из-за которого точка не доедет до часового объекта,
необратим: через две недели её неоткуда взять. Пропуск, из-за которого сервис
упадёт, — обратим, телефон дошлёт. Взвешивай в эту сторону.
### 2. Наблюдаемость: хватит ли сигналов
Представь, что этот код сломался, а владелец — один человек с `jq` над
JSON-логами и `/stats`. Вопрос не «логируется ли что-нибудь», а:
- по какому полю он найдёт **эту** доставку среди прочих (`delivery_id`,
`automation_id`, `session_id`) и **этот** часовой объект
(`metric`/`layer`/`hour_utc`);
- увидит ли он **причину**, а не только факт отказа;
- отличит ли штатный отказ от поломки (уровень выбран по адресату?);
- останется ли след, если операция упала **между** шагами — тело в архиве, а
строки `delivery` нет; строка есть, а разбор не дошёл.
Отдельный, самый важный для этого проекта вопрос: **виден ли сигнал о том, что
сигнала нет.** Телефон шлёт непрерывно и молча; тихо сломавшаяся автоматизация
не порождает ни одного события — она порождает их отсутствие. Событийный лог
такое не ловит по построению. Если изменение трогает приём или счётчики, спроси
прямо: по чему владелец узнает, что поток встал, и через сколько.
И обратная сторона: **данные о здоровье чувствительны.** Сигнал, который для
диагностики тащит в лог тело доставки или значения точек, — это не полезная
наблюдаемость, а утечка; тела — только `DEBUG` и с обрезкой. Отсутствующий
сигнал — находка `minor`/`major`; лишний сигнал с содержимым — находка тоже.
### 3. Что удалил бы опытный человек
Самая ценная и самая непопулярная часть. Ищи:
- **слой с единственной реализацией** — обёртка, которая ничего не добавляет,
кроме имени;
- **интерфейс, заведённый ради мока** — если вторая реализация живёт только в
тестах, интерфейс, скорее всего, лишний (в Go интерфейс объявляет
потребитель, и обычно узкий);
- **незапрошенная конфигурируемость** — параметр, который никто никогда не
менял и который спека не заказывала: каждое такое поле навсегда входит в
контракт `config.toml`, а образец обязан его объяснить;
- **подстраховка поверх подстраховки** — проверка того, что уже проверено
уровнем ниже, ретрай поверх ретрая, `if err != nil` вокруг кода, который не
может вернуть ошибку;
- **абстракция «на будущее»** — заготовка под второй источник данных, второе
хранилище, второй транспорт, которых нет и не запланировано;
- **самодеятельная нормализация** — переименование поля Apple, пересчёт единиц,
отбрасывание незнакомого ключа внутри точки. Это не лишний код, это нарушение
инварианта дословности, но обнаруживается тем же взглядом.
Важно: это **тот же класс дефекта**, который писала породившая код модель, и
она считает его нормой — «так выглядит хороший код». Поэтому обосновывай
удаление ценой: сколько мест придётся тронуть при следующем изменении, что
именно перестанет быть очевидным.
### 4. Пять вопросов второго инженера
Ровно пять вопросов, которые задаст второй инженер, читая этот код, и ответ на
которые **не следует из кода**. Не риторические, а настоящие: «что произойдёт,
если в доставке приедет метрика с формой точки, которой нет ни в одном пакете
из `testdata`?», «две доставки попали в один и тот же `hour_utc` одновременно —
чьи точки останутся?».
Вопрос, на который в коде нет ответа, — это либо отсутствующий комментарий
«почему», либо необдуманный случай. Раздели их сам.
## Что читать
Дифф, затронутые файлы целиком, соседние стадии того же потока — приём, разбор,
слияние, чтение — чтобы понять, что считается «зрелым» в этом проекте;
`openspec/specs/<capability>/` для понимания назначения. `docs/architecture.md`
и `docs/local-research.md` — чтобы отличить сознательно не сделанное от
забытого: часть пропусков там уже объяснена. Конвенции логирования
(`docs/conventions.md`) — по мере надобности для пункта 2.
## Чего этот проход принципиально не может поймать
- Дефекты в написанном: ты смотришь на отсутствующее, ошибку в существующей
строке пропустишь.
- Что из отсутствующего **сознательно** не сделано: решение «пока не нужно»
выглядит для тебя ровно как забытое. Поэтому находки этого прохода часто
`Действие: развилка`, а не «чинить».
- Реальную нужность сигнала: без истории инцидентов ты не знаешь, что на самом
деле смотрят при разборе. Часть наблюдений живёт в `docs/local-research.md`,
но это разведка на данных, а не журнал отказов.
- Соответствие спеке и рантайм.
## Формат вывода
1. `## Чего нет` — находки по контракту.
2. `## Наблюдаемость` — находки по контракту.
3. `## Что удалил бы` — находки по контракту, каждая с ценой сохранения.
4. `## Пять вопросов второго инженера` — список из пяти, с пометкой
«нужен комментарий почему» или «случай не обдуман».
5. Обязательный блок:
```
## Coverage of this pass
- проверено: <какие узлы, с чем сравнивалась зрелость>
- не проверялось и почему: ...
- принципиально недоступно этому проходу: сознательность пропусков, история инцидентов, ошибки в написанном коде
```
## Ограничения
Только чтение. Код не редактируй. Не предлагай удалять то, на что ссылается
дельта-спека, — это находка в спеку и всегда развилка. Не предлагай удалять
дословность хранения точки как «избыточность»: на ней держится срок жизни
данных.
+6 -1
View File
@@ -78,7 +78,12 @@ VPS **rivendell**: один бинарь в контейнере, перед н
объект прочитан и слит, но не записан; ретеншен удалил файл, а пометку не
поставил. Что останется? Кто это подберёт при следующем старте — и подберёт
ли вообще, или это чинится только ручным `reindex`?
7. **Наблюдаемость.** Хватит ли записей в JSON-логе, чтобы восстановить цепочку
7. **Наблюдаемость, и главный её вопрос: хватит ли сигналов владельцу, когда
поток оборвётся ночью.** Спрашивается не «есть ли лог», а увидит ли человек
факт — не залезая в SQLite и не читая `docker logs` построчно. Вопрос
переехал сюда из упразднённого прохода про негативное пространство, поэтому
отвечай на него отдельно и до остальных частей пункта.
Хватит ли записей в JSON-логе, чтобы восстановить цепочку
по `delivery_id`? Отличим ли штатный отказ от поломки по уровню? Виден ли
в `/stats` факт **тишины** — что поток по автоматизации прекратился, а не
просто нет новых событий? И зеркальный вопрос: не утекают ли в лог тело
@@ -14,6 +14,13 @@ color: purple
Находки — по контракту
`.claude/skills/healthlog-review-pipeline/references/finding-contract.md`.
**Тебя запускают по триггеру, а не всегда.** Триггер один: изменение вводит
**новое правило слияния, идентичности или разбора**. Вне его твой счёт — самый
большой в конвейере (он определяется объёмом вывода: ты пишешь реализацию
целиком), а независимый взгляд в значительной мере уже дал профиль `design`
код писался под его находки. Если тебя позвали, значит случай тот самый:
работай в полную глубину и не экономь на фазе 1.
## Фаза 1 — своя реализация. Существующую открывать ЗАПРЕЩЕНО
Тебе дают: требования из дельта-спеки, сигнатуры соседей, с которыми узел