удалены проектные копии скиллов и агентов ревью
- девять агентов healthlog-review-* и скиллы healthlog-{review,task}-pipeline
переехали в плагины av-dev-pipeline и av-dev-pm
- плагины av-dev-git, av-dev-pm, av-dev-pipeline включены в settings.json
This commit is contained in:
@@ -1,400 +0,0 @@
|
||||
---
|
||||
name: healthlog-review-pipeline
|
||||
description: Конвейер ревью изменений healthlog — детерминированный гейт, сверка с дельта-спеками OpenSpec в обе стороны, враждебные постановки и эксплуатационный постмортем, независимая реализация по триггеру, архитектура и обязательный триаж. Проходы гонятся последовательно; параллельно — только по явной просьбе и с явно названным набором. Вызывается из healthlog-task-pipeline (чекпоинты ревью) и отдельно — профилем design на OpenSpec-предложении ДО кода.
|
||||
---
|
||||
|
||||
# Конвейер ревью (healthlog)
|
||||
|
||||
Готовит ревью — **не заменяет его**. Потребитель отчёта — оркестратор, который
|
||||
чинит код; человек читает только сводку, развилки и границы покрытия.
|
||||
|
||||
## Три правила, из которых всё следует
|
||||
|
||||
Если ситуация не покрыта инструкцией — решай по ним.
|
||||
|
||||
1. **Recall чек-листа равен длине чек-листа.** Проход, устроенный как «проверь
|
||||
пункты 1..N», найдёт ровно перечисленное. Всё неявное — идиомы, форма
|
||||
решения, «так не делают» — неперечислимо по определению: перечислимое уже
|
||||
стало бы конвенцией. Отсюда деление проходов на **applicative** (применяют
|
||||
заданный критерий) и **generative** (сперва порождают критерий или
|
||||
альтернативу, потом сравнивают). Расширять чек-листы бесполезно; неявный слой
|
||||
достают только generative-проходы.
|
||||
2. **Ценность верификатора = наличие внешнего оракула × декорреляция с
|
||||
автором**, а не число ролей. Под всеми ролями одна модель с одними
|
||||
априорными, вход у всех общий: седьмая роль почти не добавляет recall, но
|
||||
линейно удорожает триаж. Иерархия надёжности: детерминированный инструмент >
|
||||
агент, который его **запускает** и интерпретирует вывод > агент с чистым
|
||||
мнением. Максимум работы переносим вниз.
|
||||
3. **Отчёт без границ покрытия хуже отсутствия отчёта.** «Критичных проблем не
|
||||
обнаружено» потребляет ощущение проверенности, ничего не гарантируя. Секция
|
||||
границ покрытия обязательна и не сокращается — в том числе в докладе человеку.
|
||||
|
||||
## Что этот конвейер защищает в healthlog
|
||||
|
||||
Инварианты, нарушение которых — по умолчанию `critical` (подробно —
|
||||
`CLAUDE.md`, `docs/architecture.md`):
|
||||
|
||||
- **Точка хранится дословно.** Хранилище — свёртка по журналу
|
||||
(`import(экспорт) + replay(доставки)`), поэтому разобранное пересобираемо, а
|
||||
вот не принятое — нет: доставка мимо архива теряется навсегда.
|
||||
- **Идентичность по координатам** (`метрика + слой + метка`). `source` в ключ
|
||||
не входит. Неверное правило слияния портит историю молча — заметить это
|
||||
можно только сверкой с родным экспортом Apple, то есть месяцами позже.
|
||||
- **Агрегации при записи нет.** Свёртка живёт только в ответе и только с
|
||||
измеренным родом метрики. Нижний слой HAE не суммируется никогда.
|
||||
- **Данные о здоровье чувствительнее токенов.** Тело запроса в логе на уровне
|
||||
выше `DEBUG`, файл выгрузки под контролем версий — это утечка, а не
|
||||
неаккуратность.
|
||||
- **Приём не теряет доставку.** Код ответа отражает доставку, а не разбор;
|
||||
тело ложится на диск до разбора.
|
||||
|
||||
## Модель по проходу
|
||||
|
||||
Следует из правила 2: чем больше работы делает детерминированный инструмент,
|
||||
тем дешевле может быть модель; чем больше проход **порождает** критерий, тем
|
||||
дороже. Модель задана во frontmatter каждого агента, менять её здесь не нужно.
|
||||
|
||||
| Модель | Проходы | Почему |
|
||||
|---|---|---|
|
||||
| `sonnet` | gate, code, ops | вход структурный, критерий записан заранее |
|
||||
| `opus` | specs, adversary, rubric, reimpl | суждение без опоры на инструмент |
|
||||
| `fable` | triage, architecture | ошибка распространяется дальше самой находки |
|
||||
|
||||
**Fable — только двум проходам, и это калибровка, а не осторожность.** Первый
|
||||
прогон конвейера (ревью дизайна `razbor-metrik-v-obekty`) показал, что самые
|
||||
ценные находки дали **opus**-проходы: `specs` дал 13 находок с оракулами, а
|
||||
упразднённый впоследствии `idiom` — три эксперимента против драйвера
|
||||
(`SQLITE_BUSY_SNAPSHOT` 517 против `_txlock=immediate`, куча `map[string]any`
|
||||
против `json.RawMessage`, потери `json.Marshal` без `UseNumber`). Разницы в
|
||||
пользу более дорогой модели на опиниативных проходах не обнаружилось — значит
|
||||
платить за неё там не за что.
|
||||
|
||||
Двое, у кого fable остаётся, отобраны по одному признаку: **их ошибка
|
||||
распространяется дальше собственной находки.**
|
||||
|
||||
- `triage` — через него проходит всё, что оркестратор реализует **молча**:
|
||||
ложноположительная находка становится кодом, потерянный `critical` —
|
||||
дефектом. Ошибка триажа дороже ошибки любого отдельного прохода.
|
||||
- `architecture` — запускается редко (только `deep` и `design`), потолок в
|
||||
3 находки делает его дешёвым по выходу, а находка на предложении стоит
|
||||
абзаца против переписывания на готовом коде. Дёшево × высокое плечо.
|
||||
|
||||
`reimpl` намеренно **не** в этом списке, хотя он самый ценный из generative:
|
||||
его стоимость определяется объёмом вывода (он пишет реализацию целиком), так
|
||||
что дорогая модель множит самый большой счёт. Ценность же его — в
|
||||
**независимости** взгляда, а не в мощности модели.
|
||||
|
||||
**Haiku не используется ни на одном проходе, и это не экономия наоборот.**
|
||||
Дешёвая модель на опиниативном проходе даёт правдоподобные находки, которые
|
||||
триаж обязан опровергать оракулом, — а это самая дорогая операция конвейера.
|
||||
Механизируемая же работа здесь давно вынесена **ниже** модели: `gate.py`,
|
||||
`diff-coverage.py`, `review-context.py`, `backlog.py` стоят ноль токенов.
|
||||
Дешёвому проходу просто не осталось работы.
|
||||
|
||||
Сюда же — почему `triage` на самой сильной модели, хотя он «всего лишь
|
||||
агрегирует». Через него проходит всё, что оркестратор потом **реализует
|
||||
молча**: ложноположительная находка становится кодом, потерянный `critical` —
|
||||
дефектом. Ошибка триажа дороже ошибки любого отдельного прохода.
|
||||
|
||||
Экономия при этом достигается не понижением модели, а **непуском прохода**:
|
||||
`quick` — четыре прохода, `deep` — семь. Правило выбора профиля ниже и есть
|
||||
главный рычаг стоимости.
|
||||
|
||||
## Профили
|
||||
|
||||
| Профиль | Когда | Стадии | Проходов |
|
||||
|---|---|---|---|
|
||||
| `quick` | багфикс, локальная правка, доки | 0, 1, 5 | 4 |
|
||||
| `standard` | новая функциональность в существующем пакете | 0, 1, 2, 5 | 6 |
|
||||
| `deep` | новый пакет, изменение публичного контракта, миграция БД, трогает инварианты выше | 0, 1, 2, 3, 4, 5 | 7–8 |
|
||||
| `design` | **до кода**, на OpenSpec-предложении | specs + rubric + architecture (см. ниже) | 3 |
|
||||
|
||||
**Состав сверяется по этой таблице до коммита.** Реестр из трёх-восьми
|
||||
пунктов проверяется взглядом — и это единственная защита от промаха, который
|
||||
уже случился: пропуск прохода **не отличим от прохода без находок** (гейт
|
||||
зелёный, спеки сошлись, отчёт выглядит полным), а заметить его мог бы только
|
||||
триаж, который сам заполняется тем, что ему подали. Отчёт обязан перечислять
|
||||
запущенные проходы **поимённо и с исходом**; непущенный идёт строкой «не
|
||||
запускался» в границы покрытия, а не отсутствует. Цена молчащего пропуска
|
||||
измерена: семь находок и отдельная задача на их дозакрытие
|
||||
(`docs/review-journal.md`, 2026-08-02).
|
||||
|
||||
Правило выбора профиля — по факту изменения, не по ощущению важности:
|
||||
|
||||
- есть миграция в `internal/store/migrations/`, новый пакет `internal/*`,
|
||||
изменение контракта Read API или MCP, трогается правило слияния точек или
|
||||
вывод слоя → `deep`;
|
||||
- иначе меняется поведение, видимое снаружи (эндпоинт, форма ответа, код
|
||||
ответа приёма, формат лога) → `standard`;
|
||||
- иначе → `quick`.
|
||||
|
||||
Профиль объявляется в отчёте. Понижение профиля — решение оркестратора, и оно
|
||||
попадает в границы покрытия строкой «профиль понижен до X, потому что …».
|
||||
|
||||
## Режим запуска: параллельно или последовательно
|
||||
|
||||
Профиль отвечает «какие проходы», режим — «как их запускать». Стадии всегда идут
|
||||
по порядку номеров; выбор касается только проходов **внутри** стадии.
|
||||
|
||||
| Режим | Как | Когда |
|
||||
|---|---|---|
|
||||
| **последовательно** (умолчание) | по одному, следующий стартует после отчёта предыдущего | всегда, пока не попросили иначе |
|
||||
| **параллельно** | названные проходы — одним сообщением | только по явной просьбе **и** с явно названным набором |
|
||||
|
||||
**Умолчание — последовательно, и его не надо обосновывать.** Обосновывается
|
||||
отступление.
|
||||
|
||||
**Параллельный режим включается при двух условиях сразу**, и второе так же
|
||||
обязательно, как первое:
|
||||
|
||||
1. **о нём попросили явно** — «гони параллельно», а не «сделай побыстрее»;
|
||||
2. **названо, что именно гнать параллельно** — поимённый набор проходов
|
||||
(«`specs` и `code` параллельно») или стадия целиком («стадию 1 параллельно»).
|
||||
|
||||
Просьба без набора — **не основание**: гоним последовательно и одной строкой
|
||||
говорим, что набор не был назван. Это не придирка к формулировке. Параллелить
|
||||
можно ровно то, что не мешает друг другу, а знание об этом лежит у того, кто
|
||||
просит: он видит, занята ли машина, и ждёт ли он от прогона замеров. Домысливать
|
||||
набор за него — значит принять решение, которое он оставил себе.
|
||||
|
||||
Почему умолчание именно такое:
|
||||
|
||||
- **Замеры.** Проходы `adversary` и `ops` доказывают находки числами: время
|
||||
удержания блокировки, пик кучи, рост `-wal`, длительность транзакции. Два
|
||||
меряющих прохода на одной машине соревнуются за диск, CPU и за саму SQLite и
|
||||
выдают числа, которые не воспроизведутся. Это не гипотеза: находки сессии
|
||||
опираются ровно на такие замеры (5.019 с удержания блокировки при
|
||||
`busy_timeout` 5000, пик 768 МиБ на теле 40 МиБ, 7 МБ/с роста `-wal`, 1492
|
||||
тика из 5502). Число, снятое под конкурентную нагрузку от соседнего прохода, —
|
||||
это находка с испорченным оракулом, а её опровержение стоит дороже всего
|
||||
выигрыша от параллельности.
|
||||
- **Машина одна.** Рядом идёт задача, поднят сервис, гоняется `task gate` или
|
||||
`task verify:archive`.
|
||||
- **Ранний выход** возможен только при последовательном прогоне (см. ниже).
|
||||
- **Разбор самого конвейера.** Когда выясняется, почему проход чего-то не нашёл,
|
||||
порядок и изоляция важнее скорости.
|
||||
|
||||
Если параллельный режим всё же включён, в границы покрытия идёт строка: какие
|
||||
проходы шли разом и что замеры, снятые в этом прогоне, как оракул слабее.
|
||||
|
||||
**Чего режим не меняет — и это не подлежит обсуждению.** Проход **не видит**
|
||||
находок других проходов ни в каком режиме. «Последовательно» значит «по
|
||||
очереди», а не «следующий читает предыдущего». Вся ценность конвейера держится
|
||||
на декорреляции: под всеми ролями одна модель с одними априорными, и стоит
|
||||
показать ей чужой вывод — она согласится. Согласие нескольких проходов и так не
|
||||
повышает `confidence` (см. «Честный предел»); согласие **наведённое** ещё и
|
||||
маскируется под независимое подтверждение. Единственный, кто видит всё, —
|
||||
триаж, и это его работа.
|
||||
|
||||
**Ранний выход** (последовательный режим делает его возможным — это его побочная
|
||||
выгода, а не повод его выбирать). Допустимо остановить прогон, не докатив
|
||||
остаток, ровно в одном случае: находка требует **переделки формы**
|
||||
изменения, и остальные проходы будут смотреть на код, которого через час не
|
||||
станет. Тогда:
|
||||
|
||||
- прогон останавливается, находка чинится, конвейер запускается **заново с
|
||||
нулевой стадии** — а не «доезжает» остатком по старому коду;
|
||||
- незапущенные проходы идут в границы покрытия строкой «не запускался: прогон
|
||||
остановлен на <проход> из-за <находка>», поимённо;
|
||||
- триаж запускается только на полном прогоне. Отчёт триажа по половине проходов
|
||||
— ровно тот случай, который уже стоил семи находок: он выглядит полным,
|
||||
потому что агрегирует всё, что ему подали.
|
||||
|
||||
Ранний выход по находке, которая чинится в пределах существующей формы
|
||||
(`Действие: инлайн`), **не делается**: дешевле дособрать все находки и починить
|
||||
пачкой, чем гонять конвейер дважды.
|
||||
|
||||
Режим объявляется в отчёте наравне с профилем, и если он **параллельный** — с
|
||||
причиной и составом: «режим: параллельный по просьбе, одним сообщением шли
|
||||
`specs` и `code`». Последовательный режим объявляется одним словом:
|
||||
обосновывается отступление, а не умолчание.
|
||||
|
||||
## Стадия 0 — Gate (обязательна во всех профилях)
|
||||
|
||||
Агент `healthlog-review-gate`. Запускает `task gate` и интерпретирует вывод.
|
||||
|
||||
**Пока гейт красный — опиниативные проходы не запускаются.** Оркестратор чинит и
|
||||
перезапускает гейт. Исключение одно: отказ, унаследованный от базовой ветки
|
||||
(гейт проверяет это прогоном на базе) — тогда он фиксируется находкой и не
|
||||
блокирует.
|
||||
|
||||
Гейт возвращает не только «зелено/красно», но и находки класса **отсутствующая
|
||||
верификация**: изменённые строки без покрытия, конкурентность без теста с
|
||||
параллельным доступом, флаки-тест (не ниже `major`), недоступный инструмент.
|
||||
|
||||
Шаги выбираются по изменённым файлам: правка документации не гоняет тесты,
|
||||
линтеры и `-race`. Пропуск при этом не молчит — он виден в сводке с причиной и
|
||||
уезжает в границы покрытия, как и любой другой `SKIP`.
|
||||
|
||||
Два шага гейта специфичны для healthlog и красят его безусловно:
|
||||
`no-health-data` (файл из `data/` попал под контроль версий) и `config-samples`
|
||||
(структура конфига изменилась, а `config.example.toml`/`config.docker.toml` —
|
||||
нет).
|
||||
|
||||
## Стадия 1 — Conformance (обязательна во всех профилях)
|
||||
|
||||
Два applicative-прохода: оба применяют **записанный** критерий, оба дешёвые.
|
||||
Замеров они не делают и потому безобиднее прочих, если параллельный режим
|
||||
попросят с их именами; сами по себе идут по очереди, как и все.
|
||||
|
||||
- `healthlog-review-specs` — критерий взят из **дельта-спек change в
|
||||
`openspec/changes/<id>/specs/`**, а не из proposal, сообщения коммита или
|
||||
описания задачи. Сверка двунаправленная; направление `code → spec` важнее.
|
||||
- `healthlog-review-code` — критерий взят из `docs/conventions.md`, и только та
|
||||
его часть, которая **не выражается правилом**: механизируемое уже проверила
|
||||
стадия 0 (`sloglint`, `forbidigo`, `errorlint`, `depguard`). Уровень лога по
|
||||
адресату, единственный логирующий чекпоинт на доменной границе, трансляция
|
||||
ошибки на внешней границе, `ident.Parse` на входной границе, время в UTC
|
||||
через `store.Now()`.
|
||||
|
||||
Recall обоих равен длине их источника — это и есть предел applicative-проходов,
|
||||
ради которого существует стадия 2.
|
||||
|
||||
## Стадия 2 — Adversarial и operational (`standard`, `deep`)
|
||||
|
||||
Два прохода:
|
||||
|
||||
- `healthlog-review-adversary` — находка есть **построенный путь**, а не
|
||||
свойство;
|
||||
- `healthlog-review-ops` — постмортем от симптома у владельца сервиса к строке
|
||||
кода.
|
||||
|
||||
**Эту пару параллелить не стоит даже по просьбе — переспроси.** Оба доказывают
|
||||
находки замером, и оба меряют одно и то же железо: удержание блокировки SQLite,
|
||||
пик кучи, рост `-wal`, длительность транзакции. Запущенные разом, они портят
|
||||
числа друг другу, а испорченный оракул хуже отсутствующего: находка выглядит
|
||||
доказанной. Если их всё же назвали в параллельном наборе — выполняй, но скажи в
|
||||
границах покрытия, что числа этого прогона сняты под соседней нагрузкой.
|
||||
|
||||
**Эта стадия зарабатывает больше всех остальных вместе, и потому стоит в
|
||||
`standard`, а не только в `deep`.** Измерено на пяти задачах: враждебный проход
|
||||
дал пять из семи выживших находок дозапуска на `f8200f7` (включая обе верхние) и
|
||||
`critical` на каталоге (доставка с метками из будущего подменяла род метрики);
|
||||
эксплуатационный — единственный, кто нашёл, что откат бинаря поверх новой схемы
|
||||
стартует молча. Оба несут внешний оракул по построению: один обязан путь
|
||||
**прогнать**, второй смотрит ось времени и эксплуатации, которую не смотрит
|
||||
никто другой.
|
||||
|
||||
Для healthlog эксплуатационный проход обязан держать в голове: телефон шлёт
|
||||
непрерывно и молча, тела доходили до 42 МБ, запись в часовой объект —
|
||||
read-modify-write под конкурентными доставками, а тихо сломавшаяся
|
||||
автоматизация обнаруживается не сразу. Отдельным обязательным вопросом —
|
||||
**хватит ли сигналов владельцу, когда поток оборвётся ночью**: не «есть ли
|
||||
лог», а увидит ли человек факт, не залезая в SQLite.
|
||||
|
||||
## Стадия 3 — Independent reimplementation (`deep`, по триггеру)
|
||||
|
||||
- `healthlog-review-reimpl` — пишет свою реализацию, не открывая существующую,
|
||||
затем диффит по решениям. **Запускается по триггеру, а не всегда:** изменение
|
||||
вводит новое правило слияния, идентичности или разбора. Это самый дорогой
|
||||
проход конвейера (его счёт определяется объёмом вывода — он пишет реализацию
|
||||
целиком), а вне этого триггера независимый взгляд в значительной мере уже дал
|
||||
профиль `design`: код писался под его находки. Триггер выбран по факту:
|
||||
единственный раз, когда триаж назвал отсутствие `reimpl` дырой покрытия, —
|
||||
это была задача с новым правилом слияния сущностей.
|
||||
|
||||
## Стадия 4 — Global (`deep`, `design`)
|
||||
|
||||
Агент `healthlog-review-architecture`. Получает **вход шире диффа**: дерево
|
||||
пакетов с назначением, граф внутренних зависимостей, инвентарь существующих
|
||||
концепций проекта. Готовит вход команда:
|
||||
|
||||
```
|
||||
task review:context > tmp/review-context.md
|
||||
```
|
||||
|
||||
Главный вопрос — концептуальная целостность и **второй способ** делать то, что
|
||||
уже делается. Он же и оправдывает проход: на задаче про пересборку архитектурный
|
||||
проход нашёл, что прогон живого архива был **вторым проигрывателем журнала** со
|
||||
своим порядком. Второй обязательный вопрос — **что опытный человек отсюда
|
||||
удалил бы**: слой с единственной реализацией, интерфейс ради мока, незапрошенная
|
||||
конфигурируемость, подстраховка поверх подстраховки. Потолок — 3 находки плюс
|
||||
секция «дешевле переделать до мерджа».
|
||||
|
||||
## Стадия 5 — Triage (обязательна)
|
||||
|
||||
Агент `healthlog-review-triage`. Единственный, кто агрегирует. Получает сырые
|
||||
выводы всех проходов и `git diff`; возвращает финальный отчёт.
|
||||
|
||||
Без триажа проходы дают порядка сорока замечаний при единицах
|
||||
существенных. Потребитель здесь — оркестратор, который **молча реализует** всё,
|
||||
что прочитал: цена нетриажированного отчёта — не потерянное время человека, а
|
||||
разросшийся от вкусовщины код.
|
||||
|
||||
Порядок: дедупликация по причине → оракул для всего `critical`/`major` →
|
||||
понижение неподтверждённого до гипотезы → отсев вкусовщины → ранжирование по
|
||||
ущербу × вероятности → потолок 7 пунктов в основном списке.
|
||||
|
||||
## Профиль `design` — до кода
|
||||
|
||||
Запускается на шаге ревью спек (`healthlog-task-pipeline` шаг 4), когда change уже имеет
|
||||
`proposal.md` + дельта-спеки, но кода ещё нет. Состав:
|
||||
|
||||
1. `healthlog-review-specs` в режиме «дизайн ДО кода»;
|
||||
2. `healthlog-review-rubric`, фаза 1 без фазы 2: рубрика на задуманный узел
|
||||
становится приёмочными критериями и уезжает в `tasks.md`;
|
||||
3. `healthlog-review-architecture` на предложении: вводит ли change новое
|
||||
понятие, можно ли выразить существующими — **включая конструкции stdlib**, —
|
||||
не появляется ли второй способ. Вопрос «не изобретаем ли то, что уже есть в
|
||||
библиотеке» переехал сюда из упразднённого прохода про идиоматичность;
|
||||
4. вопрос автору дизайна: **«предложи три формы решения и назови компромисс
|
||||
каждой»** — если ответ показывает, что рассматривалась одна, это находка.
|
||||
|
||||
Смысл профиля: архитектурная находка на готовом коде стоит переписывания и
|
||||
поэтому игнорируется; та же находка на предложении стоит абзаца обсуждения.
|
||||
|
||||
## Контракт находок
|
||||
|
||||
Единый для всех проходов — [references/finding-contract.md](references/finding-contract.md).
|
||||
Коротко: заголовок через **последствие**, обязательные поля `Файл`, `Severity`,
|
||||
`Confidence`, `Оракул`, `Последствие`, `Предложение`, `Найдено проходом`.
|
||||
`critical` без оракула или построенного пути не существует. Находка без поля
|
||||
«Последствие» не выводится вовсе.
|
||||
|
||||
Каждый проход завершает вывод блоком `## Coverage of this pass`.
|
||||
|
||||
## Что происходит с находками дальше
|
||||
|
||||
- Оркестратор чинит помеченное `Действие: инлайн` и **не логирует мелочь**.
|
||||
- `Действие: развилка` — блокером в секцию `блокеры` беклога, вопросом с
|
||||
вариантами и ценой каждого. Оркестратор не останавливается: он урезает
|
||||
изменение до остатка и доводит его.
|
||||
- Находка не для этого мерджа, но реальная (отложенный `major`, развилка,
|
||||
решённая «потом») — не теряется: заводится задачей через скилл `backlog`
|
||||
(интейк из ревью), с оракулом и провенансом в теле. Мелочь класса `nit` — в
|
||||
пакетный файл, а не файлом на находку.
|
||||
- `Promote candidates` — по процедуре
|
||||
[references/promote.md](references/promote.md): находка → конвенция → правило
|
||||
линтера → **удаление из конвенций и из промптов**. Третий шаг обязателен.
|
||||
- Дефект, проскочивший ревью и всплывший позже, идёт в
|
||||
[docs/review-journal.md](../../../docs/review-journal.md) — сразу, не
|
||||
ретроспективно: теряется именно причина непоймания.
|
||||
|
||||
## Честный предел
|
||||
|
||||
Модель воспроизводит медиану публичного Go, смещённую к популярному и
|
||||
туториальному: отсюда тяга к интерфейсам ради интерфейсов, лишним мокам и
|
||||
конфигурируемости, которую никто не просил. **«Идиоматично» и «распространено» —
|
||||
разные вещи**; проходы обязаны различать их и опираться на поимённое положение
|
||||
гайда, а не на ощущение частотности.
|
||||
|
||||
Согласие нескольких проходов — **не подтверждение**: это один источник,
|
||||
высказавшийся несколько раз. Совпадение повышает приоритет, но не `confidence`.
|
||||
|
||||
Ни одному проходу принципиально недоступно:
|
||||
|
||||
- поведение Health Auto Export на следующем обновлении приложения;
|
||||
- то, что реально лежит в Apple Health, — сверить можно только с ручным
|
||||
экспортом, а он делается раз в 2–3 месяца;
|
||||
- поведение таблицы SQLite под объёмом нескольких лет истории;
|
||||
- завязка внешних потребителей (агент-медик, трекер, игра) на текущую форму
|
||||
ответа;
|
||||
- суждение «этой метрики не должно существовать».
|
||||
|
||||
Это и есть причина, по которой конвейер готовит ревью, а не заменяет его.
|
||||
|
||||
## Ссылки
|
||||
|
||||
- [references/finding-contract.md](references/finding-contract.md) — контракт находок.
|
||||
- [references/promote.md](references/promote.md) — промоут находка → конвенция → правило → удаление.
|
||||
- [docs/review-journal.md](../../../docs/review-journal.md) — журнал проскочивших дефектов.
|
||||
@@ -1,85 +0,0 @@
|
||||
# Контракт находок
|
||||
|
||||
Единый формат для всех проходов конвейера ревью. Проход, нарушивший контракт,
|
||||
считается сломанным — триаж вправе выбросить его вывод целиком.
|
||||
|
||||
## Форма находки
|
||||
|
||||
```
|
||||
### <краткая формулировка ПОСЛЕДСТВИЯ, не симптома>
|
||||
- Файл: 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, либо решение
|
||||
трогает инвариант: уезжает блокером в беклог вопросом с вариантами и ценой
|
||||
каждого, а работа продолжается на остатке.
|
||||
|
||||
Потребитель отчёта — оркестратор, который **реализует прочитанное**. Поэтому
|
||||
потолок в 7 пунктов — не забота о внимании читателя, а защита кодовой базы от
|
||||
правок, которых никто не заказывал.
|
||||
@@ -1,89 +0,0 @@
|
||||
# Промоут: находка → конвенция → правило → удаление
|
||||
|
||||
Механизм храповика. Без него конвейер выдаёт одни и те же находки бесконечно, а
|
||||
конвенции не растут — то есть внимание тратится повторно на уже решённое.
|
||||
|
||||
Роли уровней:
|
||||
|
||||
- **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) как «признано
|
||||
неавтоматизируемым».
|
||||
@@ -1,237 +0,0 @@
|
||||
---
|
||||
name: healthlog-task-pipeline
|
||||
description: Автономно проводит задачу healthlog через полный цикл SDD — от выбора в беклоге до коммита (opsx explore→propose→ревью спек→apply→ревью кода→archive→чистка беклога). Использовать, когда пользователь просит взять/сделать задачу из беклога или довести идею до реализации.
|
||||
---
|
||||
|
||||
# Пайплайн задачи (healthlog)
|
||||
|
||||
Оркестратор одной задачи по Spec Driven Development: проводит её от беклога до
|
||||
коммита максимально автономно, привлекая пользователя **только на реальных
|
||||
развилках** (компромиссы, изменение scope, угроза инвариантам). Механику не
|
||||
согласовываем — делаем.
|
||||
|
||||
Перед стартом прочитай `CLAUDE.md`, а также `README.md`, `docs/architecture.md`,
|
||||
`docs/conventions.md`, если ещё не в контексте. Это тонкая обёртка над
|
||||
каноническими скиллами `opsx:explore` / `opsx:propose` / `opsx:apply` /
|
||||
`opsx:archive` — вызывай их через Skill, не переизобретай их шаги.
|
||||
|
||||
## Что нельзя сломать
|
||||
|
||||
healthlog — хранилище данных о здоровье, у которого источник (телефон) шлёт
|
||||
непрерывно и молча. Отсюда особенности, которых нет в обычном сервисе:
|
||||
|
||||
- **Поток не останавливается на время задачи.** Сервис поднят в контейнере
|
||||
(`task up` / `task restart`), данные в `./data`. Перезапуск на пару секунд
|
||||
безопасен — дыру закроют средний и глубокий проходы синхронизации; а вот
|
||||
сломанный приём, оставленный работать, теряет данные необратимо.
|
||||
- **Потерянная доставка не восстанавливается.** Тело, не попавшее в архив, в
|
||||
журнал не попадает вовсе: телефон его не перешлёт. Разобранное же всегда
|
||||
пересобираемо свёрткой, поэтому цена ошибки разбора и цена ошибки приёма
|
||||
различаются на порядок. Любая правка разбора, слияния или вывода слоя — это
|
||||
`deep`-профиль ревью, без исключений.
|
||||
- **Данные чувствительны.** Ничего из `./data` не попадает ни в git, ни в
|
||||
логи выше `DEBUG`, ни в вывод агента. Гейт проверяет первое механически
|
||||
(`no-health-data`), остальное — на тебе.
|
||||
- **Разведка уже проведена.** `docs/local-research.md` — 46 находок на живом
|
||||
потоке, половина расходится с документацией HAE. Проверь там, прежде чем
|
||||
строить догадку о формате: скорее всего вопрос уже закрыт измерением.
|
||||
|
||||
## Принцип автономности
|
||||
|
||||
**Умолчание — делать, а не спрашивать.** Задача доводится до коммита без
|
||||
участия человека; предполагается, что так пройдёт большинство задач.
|
||||
|
||||
Наткнулся на вопрос, который решать не тебе, — **не останавливайся и не
|
||||
спрашивай**. Вынь его блокером и продолжай:
|
||||
|
||||
1. Заведи пункт в секции `блокеры` беклога:
|
||||
`backlog.py add --slug <slug> --priority блокеры --hook <что заблокировано>`.
|
||||
Тело отвечает на три вопроса: **что именно решить**, **какие есть варианты
|
||||
и цена каждого**, **что стоит, пока решения нет**. Плюс твоя рекомендация —
|
||||
человек чаще соглашается, чем выбирает заново, и готовое суждение экономит
|
||||
ему весь контекст.
|
||||
2. **Переформулируй задачу на остаток** — то, что делается без этого решения.
|
||||
Впиши в её тело ссылку на блокер и границу: докуда доводим сейчас.
|
||||
3. Доведи остаток до конца и закоммить. Задача не «висит на вопросе», она
|
||||
сделана в объявленных границах.
|
||||
|
||||
Если полезного остатка нет вовсе — блокер заводится, задача остаётся на месте
|
||||
со ссылкой на него, и берётся следующая. Это редкий случай; чаще остаток есть.
|
||||
|
||||
Блокеры разбираются пачками, а не по одному: прерывать поток ради каждого
|
||||
дороже, чем накопить.
|
||||
|
||||
### Когда всё-таки спрашивать
|
||||
|
||||
Узко и по другому основанию — не «сложное решение», а **необратимое действие**:
|
||||
|
||||
- деплой, выкладка наружу, смена публичного адреса или токенов;
|
||||
- удаление или перезапись данных в `./data`, включая подрезку архива;
|
||||
- всё, что уходит за пределы машины.
|
||||
|
||||
Здесь ошибка не откатывается коммитом, поэтому спрашиваем даже когда решение
|
||||
кажется очевидным. Развилка в дизайне — блокер; необратимое действие — вопрос.
|
||||
|
||||
Стиль правок — заточка под проект и конвенции, right-size, без золочения.
|
||||
|
||||
## Шаги
|
||||
|
||||
### 1. Выбрать / прочитать задачу
|
||||
|
||||
- Если задача задана (slug, файл в `docs/backlog/` или описание) — прочитай её
|
||||
файл и связанные спеки/черновики.
|
||||
- Если не задана — выбирай сам: верхняя секция приоритета, не `[idea]`, не
|
||||
заблокированная целиком. Из равных бери ту, что разблокирует больше других.
|
||||
Выбор объявляешь в докладе, а не согласовываешь заранее.
|
||||
- Задача с префиксом `[idea]` (ещё без решения «делаем») — сперва обязательно
|
||||
через explore (шаг 2), там она либо становится задачей, либо остаётся идеей.
|
||||
|
||||
Формат файла задачи и индекса держит скилл `backlog` — здесь мы беклог только
|
||||
читаем. Если по ходу выбора вскрылось, что задача устарела, дублируется или
|
||||
разрослась в эпик, это работа для скилла `backlog`, а не для пайплайна.
|
||||
|
||||
Оцени тривиальность (влияет на шаг 4):
|
||||
- **Тривиальная** — локальная правка без изменения поведения/спек/схемы БД,
|
||||
очевидное решение. Explore и ревью спек пропускаем.
|
||||
- **Нетривиальная** — новое/изменённое поведение, дизайн-развилки, затрагивает
|
||||
инварианты, схему БД или несколько capability. Полный цикл.
|
||||
|
||||
### 2. (Опц.) Груммить идею — `opsx:explore`
|
||||
|
||||
Только для `[idea]`-задач или когда постановка мутная. Вызови Skill
|
||||
`opsx:explore`. Развилку грумминга не выноси на человека — заведи блокером и
|
||||
груми остаток. Выход: ясная постановка, готовая к propose. **В explore не
|
||||
пишем код.**
|
||||
|
||||
### 3. Завести change — `opsx:propose`
|
||||
|
||||
Вызови Skill `opsx:propose`. Получаем `proposal.md`, дизайн (для нетривиальных),
|
||||
дельта-спеки (`ADDED`/`MODIFIED`/`REMOVED Requirements`), `tasks.md`. Каждое
|
||||
`### Requirement` содержит `SHALL`/`MUST`; структурные заголовки английские,
|
||||
сценарии `GIVEN/WHEN/THEN`. Прогони `openspec validate --strict <id>`.
|
||||
|
||||
### 4. (Нетривиальная) Ревью предложения — профиль `design`, ДО кода
|
||||
|
||||
Первый чекпоинт ревью-процесса. Вызови Skill **`healthlog-review-pipeline`** с профилем
|
||||
`design` и ссылкой на change `<id>`. Он запустит `healthlog-review-specs` (режим
|
||||
«дизайн/спеки ДО кода»), `healthlog-review-rubric` (фаза 1: приёмочные критерии
|
||||
для задуманного узла) и `healthlog-review-architecture` по предложению.
|
||||
|
||||
Смысл профиля: архитектурная находка на готовом коде стоит переписывания и
|
||||
потому игнорируется — та же находка здесь стоит абзаца обсуждения. Рубрику из
|
||||
`healthlog-review-rubric` перенеси в `tasks.md` как приёмочные критерии.
|
||||
|
||||
### 5. Отработать замечания ревью предложения
|
||||
|
||||
- Мелочь и явные улучшения — правь сам в спеках/дизайне.
|
||||
- Развилки (компромисс, scope, инвариант) — блокером, спеки урезаются на
|
||||
остаток.
|
||||
- После правок перепрогони `openspec validate --strict <id>`.
|
||||
|
||||
### 6. Написать код — `opsx:apply`
|
||||
|
||||
Вызови Skill `opsx:apply` для реализации `tasks.md`. Код по конвенциям
|
||||
`docs/conventions.md`: ошибки stdlib с `%w`/`errors.Is`, логи только `slog` без
|
||||
секретов и тел запросов, время в UTC через `store.Now()`, ULID через
|
||||
`internal/ident`, миграции goose. Меняешь схему — обнови ER-схему
|
||||
`docs/database.md` в том же change (гейт это проверяет).
|
||||
|
||||
Прогони `task gate` и добейся зелёного — он же гейт следующего шага.
|
||||
|
||||
**Поведенческая верификация.** Если задача меняет реальное поведение (новый
|
||||
эндпоинт, разбор входа, схема БД, форма ответа) — зелёных юнит-тестов мало.
|
||||
Подними изменение вживую: `task restart`, затем прогони сценарий по настоящим
|
||||
данным из `./data` (89+ доставок реального потока) или скриптом из
|
||||
`tmp/research/`. Пропусти только для чисто внутренних правок без наблюдаемого
|
||||
рантайма.
|
||||
|
||||
**Сервис не оставляем лежать.** Если `task restart` упал — почини или откати
|
||||
до конца шага: телефон продолжает слать всё это время.
|
||||
|
||||
### 7. Ревью кода — Skill `healthlog-review-pipeline`
|
||||
|
||||
Второй чекпоинт. Вызови Skill **`healthlog-review-pipeline`**, дав ссылку на change
|
||||
`<id>`, базу диффа, профиль **и режим запуска**. Профиль выбирается по факту
|
||||
изменения, а не по ощущению важности (правило — в самом скилле):
|
||||
|
||||
- миграция, новый пакет, контракт Read API или MCP, правило слияния точек или
|
||||
вывод слоя → `deep`;
|
||||
- иначе меняется поведение, видимое снаружи → `standard`;
|
||||
- иначе (багфикс, локальная правка, доки) → `quick`.
|
||||
|
||||
**Режим по умолчанию последовательный, и обосновывать его не надо.** Параллельно
|
||||
гоняем только тогда, когда об этом попросили явно **и назвали набор** — какие
|
||||
именно проходы или какую стадию. Просьба без набора основанием не считается:
|
||||
гони последовательно и скажи строкой, что набор не был назван. Причина умолчания
|
||||
— замеры: `adversary` и `ops` доказывают находки числами (удержание блокировки,
|
||||
пик кучи, рост `-wal`), а два меряющих прохода на одной машине портят числа друг
|
||||
другу; находка с испорченным оракулом хуже отсутствующей, потому что выглядит
|
||||
доказанной. Правило целиком и его оговорки — в самом скилле.
|
||||
|
||||
Скилл сам гоняет гейт, нужные проходы и обязательный триаж. Возвращает отчёт с
|
||||
потолком 7 пунктов, разметкой `Действие: инлайн | развилка` и секцией границ
|
||||
покрытия.
|
||||
|
||||
**Сверь состав прогона с таблицей профилей в скилле, прежде чем коммитить.**
|
||||
Пропуск прохода не отличим от прохода без находок: гейт зелёный, спеки сошлись,
|
||||
отчёт выглядит полным. Единственный, кто мог бы заметить пропуск, — триаж, а он
|
||||
заполняется тем, что ему подали. Отчёт обязан называть запущенные проходы
|
||||
**поимённо и с исходом**; непущенный идёт строкой «не запускался» в границы
|
||||
покрытия. Реестр короткий (4–8 проходов) — сверка стоит одного взгляда, а
|
||||
молчащий пропуск уже стоил семи находок и отдельной задачи на их дозакрытие.
|
||||
|
||||
Отработай так же, как шаг 5: помеченное `инлайн` чини сам и не логируй,
|
||||
`развилка` — блокером в беклог (вопрос уже сформулирован триажем, его остаётся
|
||||
перенести). После правок — снова `task gate`.
|
||||
|
||||
**Границы покрытия из отчёта не выбрасывай** — они уезжают в финальный доклад
|
||||
(шаг 10) сжатой строкой. Отчёт, из которого исчезло «что проверить было
|
||||
невозможно», превращается в ложное ощущение проверенности.
|
||||
|
||||
### 8. Архивировать — `opsx:archive`
|
||||
|
||||
Вызови Skill `opsx:archive`: change уезжает в `openspec/changes/archive/`,
|
||||
дельты вливаются в `openspec/specs/`.
|
||||
|
||||
### 9. Закрыть беклог и синк доков
|
||||
|
||||
Ревью выполненного — **до** чистки. Затем:
|
||||
|
||||
- Удали файл задачи `docs/backlog/<slug>.md` и строку в `docs/backlog/README.md`.
|
||||
Реализованное не держим в беклоге — у него есть коммит и спека.
|
||||
- Суть переехавшего решения — в `docs/architecture.md`, если ещё не там.
|
||||
- Менялась структура БД — убедись, что `docs/database.md` обновлён в этом же
|
||||
change.
|
||||
- Новое, узнанное о формате HAE или о данных, — в `docs/local-research.md`
|
||||
очередной находкой. Это источник истины по формату, и он ценнее кода.
|
||||
- Проверь согласованность индекса командой `check` скилла `backlog`.
|
||||
|
||||
### 10. Коммит
|
||||
|
||||
Коммить **в текущую ветку** (`git rev-parse --abbrev-ref HEAD`), сам ветку не
|
||||
создавай и не переключай, ничего не пушь. При ручном запуске HEAD обычно на
|
||||
`master` — коммит идёт прямо в него, без feature-веток.
|
||||
|
||||
Сообщение — по-русски, по скиллу `commit` (первая строка отвечает «что
|
||||
сделано», тело списком 1–3 пункта, без трейлеров). Одна задача — один
|
||||
осмысленный коммит.
|
||||
|
||||
Готово — доложи пользователю кратко: что сделано, какие блокеры заведены и
|
||||
чем ограничен остаток, ссылка на архивный change. **Плюс одна строка границ покрытия** из отчёта
|
||||
ревью: какой профиль гонялся и что проверить было невозможно. Доклад без неё
|
||||
сообщает «проверено», не сообщая, что именно.
|
||||
|
||||
## Тонкости
|
||||
|
||||
- **Не завязывайся на master и корень репо.** Скилл работает в текущем worktree
|
||||
и на текущей ветке: не делай `git checkout`/`switch`, не создавай веток, не
|
||||
пушь.
|
||||
- Не пропускай `openspec validate --strict` перед архивацией.
|
||||
- Тривиальная задача: шаги 2 и 4 пропускаются; ревью кода (шаг 7) остаётся
|
||||
всегда, но в профиле `quick`.
|
||||
- Гейт блокирует: пока `task gate` красный, опиниативные проходы не
|
||||
запускаются. Чинить и перезапускать, а не «посмотреть заодно».
|
||||
- Если ревью предлагает крупную переработку — это развилка: не правь молча и
|
||||
не спрашивай, заведи блокером и доведи остаток.
|
||||
- Держи пользователя в цикле короткими репликами на переходах фаз, но не проси
|
||||
подтверждать механику.
|
||||
Reference in New Issue
Block a user