ревью: последовательный режим стал умолчанием

- параллельно гоняем только по явной просьбе И с явно названным набором
  проходов или стадии; просьба без набора основанием не считается
- обосновывается отступление, а не умолчание
- пару adversary + ops не параллелим даже по просьбе без переспроса:
  оба меряют одно железо, испорченный оракул хуже отсутствующего
- параллельный прогон объявляется в отчёте с составом, а замеры из него
  идут в границы покрытия как ослабленные
This commit is contained in:
av
2026-08-02 20:59:11 +03:00
parent 58cf5c07d8
commit 7e6d63415e
2 changed files with 60 additions and 44 deletions
@@ -1,6 +1,6 @@
--- ---
name: healthlog-review-pipeline name: healthlog-review-pipeline
description: Конвейер ревью изменений healthlog — детерминированный гейт, сверка с дельта-спеками OpenSpec в обе стороны, враждебные постановки и эксплуатационный постмортем, независимая реализация по триггеру, архитектура и обязательный триаж. Проходы гонятся параллельно или последовательно (второе — когда ожидаются замеры или занята машина). Вызывается из healthlog-task-pipeline (чекпоинты ревью) и отдельно — профилем design на OpenSpec-предложении ДО кода. description: Конвейер ревью изменений healthlog — детерминированный гейт, сверка с дельта-спеками OpenSpec в обе стороны, враждебные постановки и эксплуатационный постмортем, независимая реализация по триггеру, архитектура и обязательный триаж. Проходы гонятся последовательно; параллельно — только по явной просьбе и с явно названным набором. Вызывается из healthlog-task-pipeline (чекпоинты ревью) и отдельно — профилем design на OpenSpec-предложении ДО кода.
--- ---
# Конвейер ревью (healthlog) # Конвейер ревью (healthlog)
@@ -138,31 +138,44 @@ description: Конвейер ревью изменений healthlog — дет
| Режим | Как | Когда | | Режим | Как | Когда |
|---|---|---| |---|---|---|
| **параллельно** (умолчание) | все проходы стадии — одним сообщением | обычный случай: проходы только читают код | | **последовательно** (умолчание) | по одному, следующий стартует после отчёта предыдущего | всегда, пока не попросили иначе |
| **последовательно** | по одному, следующий стартует после отчёта предыдущего | причины ниже, любой одной достаточно | | **параллельно** | названные проходы — одним сообщением | только по явной просьбе **и** с явно названным набором |
Последовательный режим выбирается, когда верно хоть что-то из: **Умолчание — последовательно, и его не надо обосновывать.** Обосновывается
отступление.
0. **Его попросили.** Владелец сказал «гони последовательно» — этого достаточно, **Параллельный режим включается при двух условиях сразу**, и второе так же
обоснования не требуется и переспрашивать не надо. Просьба перекрывает любую обязательно, как первое:
эвристику ниже, в том числе когда по ним вышло бы «параллельно». В отчёте —
строкой «режим: последовательный, по просьбе». Симметрично: явная просьба 1. **о нём попросили явно** — «гони параллельно», а не «сделай побыстрее»;
гнать параллельно перекрывает пункты 1–4, и тогда в границы покрытия идёт 2. **названо, что именно гнать параллельно** — поимённый набор проходов
оговорка, что замеры сняты под конкурентной нагрузкой и как оракул слабее. `specs` и `code` параллельно») или стадия целиком («стадию 1 параллельно»).
1. **Ожидаются замеры.** Проход измеряет время удержания блокировки, пик кучи,
рост `-wal`, длительность транзакции, пропускную способность. Два таких Просьба без набора — **не основание**: гоним последовательно и одной строкой
прохода, запущенных разом на одной машине, соревнуются за диск, CPU и за саму говорим, что набор не был назван. Это не придирка к формулировке. Параллелить
SQLite — и выдают числа, которые не воспроизведутся. Это не гипотеза: можно ровно то, что не мешает друг другу, а знание об этом лежит у того, кто
находки сессии опираются ровно на такие замеры (5.019 с удержания блокировки просит: он видит, занята ли машина, и ждёт ли он от прогона замеров. Домысливать
при `busy_timeout` 5000, пик 768 МиБ на теле 40 МиБ, 7 МБ/с роста `-wal`, набор за него — значит принять решение, которое он оставил себе.
1492 тика из 5502). Число, снятое под конкурентную нагрузку от соседнего
прохода, — это находка с испорченным оракулом, а её опровержение стоит Почему умолчание именно такое:
дороже, чем весь выигрыш от параллельности.
2. **Машина занята.** Идёт другая задача, поднят сервис, гоняется - **Замеры.** Проходы `adversary` и `ops` доказывают находки числами: время
`task verify:archive` (минута) или `task gate` (несколько минут). удержания блокировки, пик кучи, рост `-wal`, длительность транзакции. Два
3. **Нужен ранний выход.** См. ниже. меряющих прохода на одной машине соревнуются за диск, CPU и за саму SQLite и
4. **Разбирается сам конвейер.** Когда выясняется, почему проход что-то не выдают числа, которые не воспроизведутся. Это не гипотеза: находки сессии
нашёл, порядок и изоляция важнее скорости. опираются ровно на такие замеры (5.019 с удержания блокировки при
`busy_timeout` 5000, пик 768 МиБ на теле 40 МиБ, 7 МБ/с роста `-wal`, 1492
тика из 5502). Число, снятое под конкурентную нагрузку от соседнего прохода, —
это находка с испорченным оракулом, а её опровержение стоит дороже всего
выигрыша от параллельности.
- **Машина одна.** Рядом идёт задача, поднят сервис, гоняется `task gate` или
`task verify:archive`.
- **Ранний выход** возможен только при последовательном прогоне (см. ниже).
- **Разбор самого конвейера.** Когда выясняется, почему проход чего-то не нашёл,
порядок и изоляция важнее скорости.
Если параллельный режим всё же включён, в границы покрытия идёт строка: какие
проходы шли разом и что замеры, снятые в этом прогоне, как оракул слабее.
**Чего режим не меняет — и это не подлежит обсуждению.** Проход **не видит** **Чего режим не меняет — и это не подлежит обсуждению.** Проход **не видит**
находок других проходов ни в каком режиме. «Последовательно» значит «по находок других проходов ни в каком режиме. «Последовательно» значит «по
@@ -173,8 +186,9 @@ description: Конвейер ревью изменений healthlog — дет
маскируется под независимое подтверждение. Единственный, кто видит всё, — маскируется под независимое подтверждение. Единственный, кто видит всё, —
триаж, и это его работа. триаж, и это его работа.
**Ранний выход.** В последовательном режиме допустимо остановить прогон, не **Ранний выход** (последовательный режим делает его возможным — это его побочная
докатив остаток, ровно в одном случае: находка требует **переделки формы** выгода, а не повод его выбирать). Допустимо остановить прогон, не докатив
остаток, ровно в одном случае: находка требует **переделки формы**
изменения, и остальные проходы будут смотреть на код, которого через час не изменения, и остальные проходы будут смотреть на код, которого через час не
станет. Тогда: станет. Тогда:
@@ -190,9 +204,10 @@ description: Конвейер ревью изменений healthlog — дет
(`Действие: инлайн`), **не делается**: дешевле дособрать все находки и починить (`Действие: инлайн`), **не делается**: дешевле дособрать все находки и починить
пачкой, чем гонять конвейер дважды. пачкой, чем гонять конвейер дважды.
Режим объявляется в отчёте наравне с профилем, и если он последовательный — с Режим объявляется в отчёте наравне с профилем, и если он **параллельный**с
причиной. Строка «режим: последовательный, ожидались замеры удержания причиной и составом: «режим: параллельный по просьбе, одним сообщением шли
блокировки» стоит ничего и объясняет, почему прогон занял втрое дольше. `specs` и `code`». Последовательный режим объявляется одним словом:
обосновывается отступление, а не умолчание.
## Стадия 0 — Gate (обязательна во всех профилях) ## Стадия 0 — Gate (обязательна во всех профилях)
@@ -219,8 +234,8 @@ description: Конвейер ревью изменений healthlog — дет
## Стадия 1 — Conformance (обязательна во всех профилях) ## Стадия 1 — Conformance (обязательна во всех профилях)
Два applicative-прохода: оба применяют **записанный** критерий, оба дешёвые. Два applicative-прохода: оба применяют **записанный** критерий, оба дешёвые.
По умолчанию — одним сообщением параллельно; замеров они не делают, так что Замеров они не делают и потому безобиднее прочих, если параллельный режим
последовательный режим им нужен только из-за занятой машины. попросят с их именами; сами по себе идут по очереди, как и все.
- `healthlog-review-specs` — критерий взят из **дельта-спек change в - `healthlog-review-specs` — критерий взят из **дельта-спек change в
`openspec/changes/<id>/specs/`**, а не из proposal, сообщения коммита или `openspec/changes/<id>/specs/`**, а не из proposal, сообщения коммита или
@@ -244,11 +259,12 @@ Recall обоих равен длине их источника — это и е
- `healthlog-review-ops` — постмортем от симптома у владельца сервиса к строке - `healthlog-review-ops` — постмортем от симптома у владельца сервиса к строке
кода. кода.
**Это главные кандидаты на последовательный режим.** Оба доказывают находки **Эту пару параллелить не стоит даже по просьбе — переспроси.** Оба доказывают
замером, и оба меряют одно и то же железо: удержание блокировки SQLite, пик находки замером, и оба меряют одно и то же железо: удержание блокировки SQLite,
кучи, рост `-wal`, длительность транзакции. Запущенные разом, они портят числа пик кучи, рост `-wal`, длительность транзакции. Запущенные разом, они портят
друг другу. Если ждёшь от прогона хоть один такой замер — гони их по очереди, числа друг другу, а испорченный оракул хуже отсутствующего: находка выглядит
а не одним сообщением. доказанной. Если их всё же назвали в параллельном наборе — выполняй, но скажи в
границах покрытия, что числа этого прогона сняты под соседней нагрузкой.
**Эта стадия зарабатывает больше всех остальных вместе, и потому стоит в **Эта стадия зарабатывает больше всех остальных вместе, и потому стоит в
`standard`, а не только в `deep`.** Измерено на пяти задачах: враждебный проход `standard`, а не только в `deep`.** Измерено на пяти задачах: враждебный проход
@@ -159,14 +159,14 @@ healthlog — хранилище данных о здоровье, у котор
- иначе меняется поведение, видимое снаружи → `standard`; - иначе меняется поведение, видимое снаружи → `standard`;
- иначе (багфикс, локальная правка, доки) → `quick`. - иначе (багфикс, локальная правка, доки) → `quick`.
Режим по умолчанию параллельный. **Попросили последовательный — значит **Режим по умолчанию последовательный, и обосновывать его не надо.** Параллельно
последовательный**, без обоснований и переспрашивания. Сам проси гоняем только тогда, когда об этом попросили явно **и назвали набор** — какие
**последовательный**, если ждёшь от именно проходы или какую стадию. Просьба без набора основанием не считается:
прохода замеров — времени удержания блокировки, пика кучи, роста `-wal`, гони последовательно и скажи строкой, что набор не был назван. Причина умолчания
длительности транзакции: два меряющих прохода на одной машине портят числа друг — замеры: `adversary` и `ops` доказывают находки числами (удержание блокировки,
другу, а находка с испорченным оракулом дороже сэкономленных минут. Второй пик кучи, рост `-wal`), а два меряющих прохода на одной машине портят числа друг
повод — машина занята (поднят сервис, идёт `task gate` или другу; находка с испорченным оракулом хуже отсутствующей, потому что выглядит
`task verify:archive`). Правило и его оговорки — в самом скилле. доказанной. Правило целиком и его оговорки — в самом скилле.
Скилл сам гоняет гейт, нужные проходы и обязательный триаж. Возвращает отчёт с Скилл сам гоняет гейт, нужные проходы и обязательный триаж. Возвращает отчёт с
потолком 7 пунктов, разметкой `Действие: инлайн | развилка` и секцией границ потолком 7 пунктов, разметкой `Действие: инлайн | развилка` и секцией границ