- профиль отвечает «какие проходы», режим — «как их запускать» - явная просьба владельца — достаточное основание, без обоснований и переспрашивания; перекрывает эвристики в обе стороны - главный собственный повод — ожидаемые замеры: adversary и ops меряют одно железо (блокировка SQLite, куча, рост -wal) и портят числа друг другу, а находка с испорченным оракулом дороже сэкономленных минут - декорреляция не меняется ни в каком режиме: проход не видит чужих находок, «последовательно» ≠ «читает предыдущего» - ранний выход только при переделке формы изменения, с перезапуском с нулевой стадии; триаж — только на полном прогоне
385 lines
34 KiB
Markdown
385 lines
34 KiB
Markdown
---
|
||
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, потому что …».
|
||
|
||
## Режим запуска: параллельно или последовательно
|
||
|
||
Профиль отвечает «какие проходы», режим — «как их запускать». Стадии всегда идут
|
||
по порядку номеров; выбор касается только проходов **внутри** стадии.
|
||
|
||
| Режим | Как | Когда |
|
||
|---|---|---|
|
||
| **параллельно** (умолчание) | все проходы стадии — одним сообщением | обычный случай: проходы только читают код |
|
||
| **последовательно** | по одному, следующий стартует после отчёта предыдущего | причины ниже, любой одной достаточно |
|
||
|
||
Последовательный режим выбирается, когда верно хоть что-то из:
|
||
|
||
0. **Его попросили.** Владелец сказал «гони последовательно» — этого достаточно,
|
||
обоснования не требуется и переспрашивать не надо. Просьба перекрывает любую
|
||
эвристику ниже, в том числе когда по ним вышло бы «параллельно». В отчёте —
|
||
строкой «режим: последовательный, по просьбе». Симметрично: явная просьба
|
||
гнать параллельно перекрывает пункты 1–4, и тогда в границы покрытия идёт
|
||
оговорка, что замеры сняты под конкурентной нагрузкой и как оракул слабее.
|
||
1. **Ожидаются замеры.** Проход измеряет время удержания блокировки, пик кучи,
|
||
рост `-wal`, длительность транзакции, пропускную способность. Два таких
|
||
прохода, запущенных разом на одной машине, соревнуются за диск, CPU и за саму
|
||
SQLite — и выдают числа, которые не воспроизведутся. Это не гипотеза:
|
||
находки сессии опираются ровно на такие замеры (5.019 с удержания блокировки
|
||
при `busy_timeout` 5000, пик 768 МиБ на теле 40 МиБ, 7 МБ/с роста `-wal`,
|
||
1492 тика из 5502). Число, снятое под конкурентную нагрузку от соседнего
|
||
прохода, — это находка с испорченным оракулом, а её опровержение стоит
|
||
дороже, чем весь выигрыш от параллельности.
|
||
2. **Машина занята.** Идёт другая задача, поднят сервис, гоняется
|
||
`task verify:archive` (минута) или `task gate` (несколько минут).
|
||
3. **Нужен ранний выход.** См. ниже.
|
||
4. **Разбирается сам конвейер.** Когда выясняется, почему проход что-то не
|
||
нашёл, порядок и изоляция важнее скорости.
|
||
|
||
**Чего режим не меняет — и это не подлежит обсуждению.** Проход **не видит**
|
||
находок других проходов ни в каком режиме. «Последовательно» значит «по
|
||
очереди», а не «следующий читает предыдущего». Вся ценность конвейера держится
|
||
на декорреляции: под всеми ролями одна модель с одними априорными, и стоит
|
||
показать ей чужой вывод — она согласится. Согласие нескольких проходов и так не
|
||
повышает `confidence` (см. «Честный предел»); согласие **наведённое** ещё и
|
||
маскируется под независимое подтверждение. Единственный, кто видит всё, —
|
||
триаж, и это его работа.
|
||
|
||
**Ранний выход.** В последовательном режиме допустимо остановить прогон, не
|
||
докатив остаток, ровно в одном случае: находка требует **переделки формы**
|
||
изменения, и остальные проходы будут смотреть на код, которого через час не
|
||
станет. Тогда:
|
||
|
||
- прогон останавливается, находка чинится, конвейер запускается **заново с
|
||
нулевой стадии** — а не «доезжает» остатком по старому коду;
|
||
- незапущенные проходы идут в границы покрытия строкой «не запускался: прогон
|
||
остановлен на <проход> из-за <находка>», поимённо;
|
||
- триаж запускается только на полном прогоне. Отчёт триажа по половине проходов
|
||
— ровно тот случай, который уже стоил семи находок: он выглядит полным,
|
||
потому что агрегирует всё, что ему подали.
|
||
|
||
Ранний выход по находке, которая чинится в пределах существующей формы
|
||
(`Действие: инлайн`), **не делается**: дешевле дособрать все находки и починить
|
||
пачкой, чем гонять конвейер дважды.
|
||
|
||
Режим объявляется в отчёте наравне с профилем, и если он последовательный — с
|
||
причиной. Строка «режим: последовательный, ожидались замеры удержания
|
||
блокировки» стоит ничего и объясняет, почему прогон занял втрое дольше.
|
||
|
||
## Стадия 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) — журнал проскочивших дефектов.
|