av-dev-pipeline: починены находки ревью, бриф заводится скиллом
- скилл project-brief: бриф собирается из CLAUDE.md, архитектуры, Taskfile и конвенций и показывается человеку. Раньше единственная инструкция по его созданию лежала внутри шаблона, поэтому деградированный режим был не аварийным, а единственным: critical по основанию «нарушен инвариант» недостижим ни на одной задаче - rebase перенесён внутрь worktree задачи: прежняя форма падала на занятой ветке, и агент уводил весь батч в провалившиеся с ложной причиной - контракт брифа дополнен восемью слотами; проверен заполнением на обоих проектах, незаполнимых нет. Прецедент healthlog вынут из общего charter'а в бриф — там он вмёрз вместе с числами - шов: пайплайн задачу не закрывает и записи учёта не трогает, урожай отдаёт списком, правило остатка — ссылкой на av-dev-tasks - деградированный абзац во всех девяти проходах, вопрос 9 в ops, пространство имён в вызовах, раздел предпосылок
This commit is contained in:
@@ -29,28 +29,61 @@ description: Конвейер ревью изменения — детермин
|
||||
обнаружено» потребляет ощущение проверенности, ничего не гарантируя. Секция
|
||||
границ покрытия обязательна и не сокращается — в том числе в докладе человеку.
|
||||
|
||||
## Предпосылки
|
||||
|
||||
Конвейер опирается на внешнюю обвязку и без неё работает не целиком. Проверь это
|
||||
один раз, при установке плагина в проект:
|
||||
|
||||
- **OpenSpec и скиллы `opsx:*`.** Профиль `design`, проход `review-specs` и
|
||||
вызывающий пайплайн задачи завязаны на дельта-спеки
|
||||
(`openspec/changes/<id>/specs/*/spec.md`), на актуальные спеки
|
||||
(`openspec/specs/`) и на `openspec validate --strict`. В проекте без OpenSpec
|
||||
шаги, зовущие `opsx:explore` / `opsx:propose` / `opsx:apply` / `opsx:archive`,
|
||||
упадут на «нет такого скилла», а `review-specs` останется без источника
|
||||
требований. Такой проект либо подключает OpenSpec, либо сознательно не зовёт
|
||||
`review-specs` и профиль `design` — и тогда это идёт строкой «не запускался» в
|
||||
границы покрытия, как любой другой пропуск.
|
||||
- **Бриф проекта** — см. следующий раздел. Заводится скиллом, а не руками.
|
||||
- **Проектные копии этих скиллов и агентов удаляются при установке.** Если в
|
||||
проекте уже лежат свои `.claude/skills/review-pipeline`,
|
||||
`.claude/skills/task-pipeline`, `.claude/skills/task-batch` или
|
||||
`.claude/agents/<проект>-review-*.md` — снеси их. Иначе короткое имя разрешится
|
||||
в устаревшую проектную копию, молча и без признаков подмены. По той же причине
|
||||
**скиллы этого плагина зовутся с пространством имён**:
|
||||
`av-dev-pipeline:review-pipeline`, `av-dev-pipeline:task-pipeline`,
|
||||
`av-dev-pipeline:task-batch`, `av-dev-pipeline:project-brief`.
|
||||
|
||||
## Что конвейер защищает — приходит из брифа
|
||||
|
||||
Проходы общие, а нарушать нельзя проектное. Список инвариантов, команду гейта,
|
||||
объёмы и модель угроз конвейер **не знает** — он читает их в брифе проекта:
|
||||
[references/project-brief.md](references/project-brief.md) описывает контракт,
|
||||
[references/brief-template.md](references/brief-template.md) — образец
|
||||
объёмы, прецеденты и модель угроз конвейер **не знает** — он читает их в брифе
|
||||
проекта: [references/project-brief.md](references/project-brief.md) описывает
|
||||
контракт, [references/brief-template.md](references/brief-template.md) — образец
|
||||
заполнения.
|
||||
|
||||
Разреши путь к брифу один раз, в начале прогона: путь из задания →
|
||||
`docs/review-brief.md` → `.claude/review-brief.md`. Дальше передавай готовым.
|
||||
|
||||
**Брифа нет — прогон идёт в деградированном режиме**: `critical` по основанию
|
||||
«нарушен инвариант проекта» никем не присваивается, числа объёма не
|
||||
используются, и в границы покрытия уезжает строка «брифа проекта нет». Это дыра
|
||||
покрытия, а не нейтральное умолчание.
|
||||
**Брифа нет по всем трём путям — заведи его, а не понижай прогон.** Вызови Skill
|
||||
**`av-dev-pipeline:project-brief`**: он соберёт бриф из `CLAUDE.md`, архитектуры,
|
||||
файла задач и конвенций, покажет человеку и вернёт путь. Это механика, а не
|
||||
развилка: спрашивать разрешения не нужно, и остановка прогона тут не
|
||||
предусмотрена. Заведение стоит одного шага один раз на проект — деградированный
|
||||
режим платит на каждой задаче.
|
||||
|
||||
**Деградированный режим — исход, а не умолчание.** Он включается ровно тогда,
|
||||
когда бриф завести не удалось (репозиторий на чтение, человек прямо запретил,
|
||||
инварианты вывести неоткуда): `critical` по основанию «нарушен инвариант
|
||||
проекта» никем не присваивается, числа объёма не используются, и в границы
|
||||
покрытия уезжает строка «брифа проекта нет, завести не удалось: <причина>».
|
||||
Причина обязательна — без неё строка неотличима от «мы просто не стали».
|
||||
|
||||
## Что получает каждый проход
|
||||
|
||||
Задание любому проходу состоит из шести вещей, и первые две без брифа
|
||||
бессмысленны:
|
||||
Задание любому проходу состоит из шести вещей, и первая — главная: без брифа
|
||||
проход теряет предмет проверки и уходит в деградированный режим.
|
||||
|
||||
- **бриф** — путь;
|
||||
- **бриф** — путь (разрешён или заведён на старте, см. выше);
|
||||
- **контракт находок** — путь к
|
||||
[references/finding-contract.md](references/finding-contract.md) (в
|
||||
установленном плагине — `${CLAUDE_PLUGIN_ROOT}/skills/review-pipeline/references/`);
|
||||
@@ -170,14 +203,14 @@ description: Конвейер ревью изменения — детермин
|
||||
Почему умолчание именно такое:
|
||||
|
||||
- **Замеры.** Проходы `adversary` и `ops` доказывают находки числами: время
|
||||
удержания блокировки, пик кучи, рост файлов журнала, длительность транзакции.
|
||||
Два меряющих прохода на одной машине соревнуются за диск, CPU и за саму СУБД и
|
||||
выдают числа, которые не воспроизведутся. Это не гипотеза: находки, ради
|
||||
которых правило записано, опираются ровно на такие замеры (5.019 с удержания
|
||||
блокировки при таймауте 5000 мс, пик 768 МиБ на теле 40 МиБ, 7 МБ/с роста
|
||||
журнала, 1492 тика из 5502). Число, снятое под конкурентную нагрузку от
|
||||
соседнего прохода, — это находка с испорченным оракулом, а её опровержение
|
||||
стоит дороже всего выигрыша от параллельности.
|
||||
удержания блокировки против её таймаута, пик кучи против размера тела, темп
|
||||
роста файлов журнала, длительность транзакции. Два меряющих прохода на одной
|
||||
машине соревнуются за диск, CPU и за саму СУБД и выдают числа, которые не
|
||||
воспроизведутся. Это не гипотеза: правило выведено из находок, целиком
|
||||
державшихся на таких замерах, — у каждого проекта они свои и лежат в разделе
|
||||
`## Прецеденты` его брифа. Число, снятое под конкурентную нагрузку от соседнего
|
||||
прохода, — это находка с испорченным оракулом, а её опровержение стоит дороже
|
||||
всего выигрыша от параллельности.
|
||||
- **Машина одна.** Рядом идёт задача, поднят сервис, гоняется гейт или дорогая
|
||||
проверка проекта.
|
||||
- **Ранний выход** возможен только при последовательном прогоне (см. ниже).
|
||||
@@ -205,9 +238,9 @@ description: Конвейер ревью изменения — детермин
|
||||
нулевой стадии** — а не «доезжает» остатком по старому коду;
|
||||
- незапущенные проходы идут в границы покрытия строкой «не запускался: прогон
|
||||
остановлен на <проход> из-за <находка>», поимённо;
|
||||
- триаж запускается только на полном прогоне. Отчёт триажа по половине проходов —
|
||||
ровно тот случай, который уже стоил семи находок: он выглядит полным, потому
|
||||
что агрегирует всё, что ему подали.
|
||||
- триаж запускается только на полном прогоне. Отчёт триажа по половине проходов
|
||||
выглядит полным, потому что агрегирует всё, что ему подали, — это тот же
|
||||
молчащий пропуск, что и в разделе «Профили».
|
||||
|
||||
Ранний выход по находке, которая чинится в пределах существующей формы
|
||||
(`Действие: инлайн`), **не делается**: дешевле дособрать все находки и починить
|
||||
@@ -246,8 +279,9 @@ description: Конвейер ревью изменения — детермин
|
||||
- `review-specs` — критерий взят из **дельта-спек предлагаемого изменения**, а не
|
||||
из proposal, сообщения коммита или описания задачи. Сверка двунаправленная;
|
||||
направление `code → spec` важнее.
|
||||
- `review-code` — критерий взят из файла конвенций проекта (раздел `## Карта`
|
||||
брифа), и только та его часть, которая **не выражается правилом**:
|
||||
- `review-code` — критерий взят из конвенций проекта: файла или каталога файлов,
|
||||
путь — раздел `## Карта` брифа. Берётся только та их часть, которая **не
|
||||
выражается правилом**:
|
||||
механизируемое уже проверила стадия 0. Что именно механизировано, тот же раздел
|
||||
брифа перечисляет — повторять это проходом вредно.
|
||||
|
||||
@@ -322,7 +356,8 @@ Recall обоих равен длине их источника — это и е
|
||||
|
||||
## Профиль `design` — до кода
|
||||
|
||||
Запускается на шаге ревью спек (шаг 4 скилла `task-pipeline`), когда change уже
|
||||
Запускается на шаге ревью спек (шаг 4 скилла `av-dev-pipeline:task-pipeline`),
|
||||
когда change уже
|
||||
имеет `proposal.md` и дельта-спеки, но кода ещё нет. Состав:
|
||||
|
||||
1. `review-specs` в режиме «дизайн ДО кода»;
|
||||
@@ -359,9 +394,11 @@ Recall обоих равен длине их источника — это и е
|
||||
держит вопросы (это знает вызвавший пайплайн, а не конвейер). Оркестратор не
|
||||
останавливается: он урезает изменение до остатка и доводит его.
|
||||
- Находка не для этого мерджа, но реальная (отложенный `major`, развилка,
|
||||
решённая «потом») — не теряется: заводится задачей средствами проекта, с
|
||||
оракулом и провенансом в теле. Мелочь класса `nit` — пачкой, а не записью на
|
||||
находку.
|
||||
решённая «потом»), — не теряется, но **и не заводится здесь**. Конвейер отдаёт
|
||||
её **списком урожая** в отчёте: формулировка, оракул, провенанс (какой проход,
|
||||
какой change). Заведение задач принадлежит тому, кто ведёт задачи проекта, —
|
||||
у него свой формат, своя нарезка и свои правила дублей. Мелочь класса `nit`
|
||||
идёт в урожай одной пачкой, а не записью на находку.
|
||||
- `Promote candidates` — по процедуре [references/promote.md](references/promote.md):
|
||||
находка → конвенция → правило линтера → **удаление из конвенций и из брифа**.
|
||||
Третий шаг обязателен.
|
||||
@@ -405,6 +442,7 @@ Recall обоих равен длине их источника — это и е
|
||||
|
||||
## Ссылки
|
||||
|
||||
- Skill `av-dev-pipeline:project-brief` — заведение и обновление брифа.
|
||||
- [references/project-brief.md](references/project-brief.md) — контракт брифа проекта.
|
||||
- [references/brief-template.md](references/brief-template.md) — шаблон брифа.
|
||||
- [references/finding-contract.md](references/finding-contract.md) — контракт находок.
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
# Шаблон брифа проекта
|
||||
|
||||
Скопируй в `docs/review-brief.md` и заполни. Контракт разделов — в
|
||||
[project-brief.md](project-brief.md); здесь только образец заполнения.
|
||||
Образец заполнения. Контракт разделов — в
|
||||
[project-brief.md](project-brief.md); заводит бриф по этому образцу скилл
|
||||
`av-dev-pipeline:project-brief` — руками копировать не надо, но читать полезно.
|
||||
|
||||
Курсивом даны пояснения — их из готового брифа убирают. Примеры взяты из двух
|
||||
разных проектов (коллектор данных с непрерывным потоком и связующий сервис вокруг
|
||||
@@ -26,7 +27,8 @@
|
||||
## Инварианты
|
||||
|
||||
*Проверяемое свойство + последствие + severity по умолчанию. Цитируются
|
||||
формулировкой.*
|
||||
формулировкой. Severity проект обычно не пишет — тогда она выводится по
|
||||
обратимости и помечается: «по умолчанию `critical` (выведена по обратимости)».*
|
||||
|
||||
- **Точка сохраняется дословно.** Незнакомое поле не отбрасывается, число не
|
||||
округляется при записи. Нарушение — необратимая потеря: сырой архив живёт
|
||||
@@ -70,12 +72,26 @@
|
||||
- Карта проекта для архитектурного прохода: `task review:context > tmp/review-context.md`
|
||||
- Поднять изменение вживую: `task restart`, логи — `task logs`
|
||||
- Тесты и линт: `task test`, `task lint`
|
||||
- Дорогое, вручную: `task verify:archive` (минута, живые данные)
|
||||
- **Дорогое вне гейта, с адресатом:** `task verify:archive` (минута, живые
|
||||
данные). **Кто и когда обязан:** пайплайн задачи — после любого изменения
|
||||
разбора входного формата или правила слияния, до архивации change; вручную —
|
||||
человек перед выкладкой. Не прогонялась — строка в границы покрытия, а не
|
||||
молчание.
|
||||
- **Запускать запрещено:** ничего, что пишет в `./data`, в рабочую БД и в боевой
|
||||
каталог архива. Замеры — только на копиях в `./tmp`.
|
||||
|
||||
## Прод и поток
|
||||
|
||||
*Первая строка — главный вопрос эксплуатации этого проекта.*
|
||||
|
||||
> **Главный вопрос:** поток идёт непрерывно и молча, отправитель об отказе не
|
||||
> узнает и не повторит — значит, дороже всего тихо потерянная доставка, а не
|
||||
> упавший сервис.
|
||||
|
||||
> *(В сервисе, который сам опрашивает чужих демонов, первая строка была бы
|
||||
> противоположной: «главный вопрос — что происходит, когда внешний сервис
|
||||
> отвечает медленно, а не когда он упал».)*
|
||||
|
||||
- **Где:** один статический бинарь в контейнере на домашнем сервере, перед ним
|
||||
обратный прокси с TLS, SQLite на диске. Ни оркестратора, ни реплик, ни дежурной
|
||||
смены.
|
||||
@@ -84,11 +100,17 @@
|
||||
параллельной записью; приложение-источник на телефоне — молча перестаёт слать.
|
||||
*(В другом проекте здесь были бы качалка, медиасервер, LLM и база метаданных, и
|
||||
каждая — со своим «отвечает медленно», а не только «упала».)*
|
||||
*(Если зависимостей нет — так и пишут: «внешних зависимостей нет, смотри на
|
||||
диск и на СУБД». Пустой пункт называется пустым.)*
|
||||
- **Кто заметит отказ:** один пользователь-владелец, в лучшем случае вечером, а
|
||||
скорее не заметит вовсе.
|
||||
- **Характер потока:** телефон шлёт непрерывно и молча; обратной связи у
|
||||
отправителя нет, об отказах он не сообщает, расписание плавает. Тихо
|
||||
сломавшаяся доставка — главный эксплуатационный риск.
|
||||
- **Представление данных и настройки хранилища:** запись — сжатый BLOB, читается
|
||||
и пересобирается целиком на каждой операции (`internal/store`); журнал СУБД —
|
||||
WAL; таймаут занятости — 5000 мс (`config.example.toml`); лимит тела приёма —
|
||||
64 МБ; ретеншен сырого архива — 14 дней.
|
||||
- **Числа (с провенансом):** нижний слой — порядка 135 тыс. точек в сутки
|
||||
(замер, `docs/local-research.md`); тела доходили до 42 МБ (там же); запись —
|
||||
read-modify-write под конкурентными доставками (`docs/architecture.md`).
|
||||
@@ -98,6 +120,19 @@
|
||||
|
||||
## Модель угроз
|
||||
|
||||
*Первая строка — периметр.*
|
||||
|
||||
> **Периметр:** сервис открыт наружу через обратный прокси, недоверенным считается
|
||||
> всё, что приходит по HTTP. Злоумышленник в локальной сети — вне периметра.
|
||||
|
||||
> *(У сервиса в доверенном контуре первая строка противоположна: «контур
|
||||
> доверенный, публичного интернета здесь нет — не выдумывай его; недоверенное
|
||||
> здесь — то, что отдают внешние демоны и трекеры».)*
|
||||
|
||||
> *(Контур ещё не развёрнут — тогда периметров два: «целевой — за прокси с TLS;
|
||||
> сегодняшний — только локальная машина, токены пусты осознанно. **Находки
|
||||
> строятся против целевого**, отсутствие TLS сегодня находкой не является».)*
|
||||
|
||||
- **Недоверенное:** тело доставки целиком (имена метрик, единицы, формы точек,
|
||||
метки времени, глубина вложенности, размер); заголовки доставки, часть которых
|
||||
участвует в решениях; содержимое архива внешнего экспорта (имена файлов внутри
|
||||
@@ -117,13 +152,25 @@
|
||||
|
||||
## Карта
|
||||
|
||||
- **Основная ветка:** `master`. От неё берутся ветки задач, в неё вливается батч,
|
||||
база диффа по умолчанию — `git merge-base HEAD master` (на самой ветке `HEAD~1`).
|
||||
- Актуальные спеки: `openspec/specs/<capability>/spec.md`
|
||||
- Дельта-спеки изменения: `openspec/changes/<id>/specs/*/spec.md`
|
||||
- Конвенции прозой: `docs/conventions.md`. Механизировано и потому **не
|
||||
проверяется проходом по конвенциям**: форма логов, `fmt.Print*`/`os.Getenv`/
|
||||
`time.Now` мимо единых точек, сравнение ошибок, сторонние пакеты ошибок —
|
||||
всё это правила в `.golangci.yml`.
|
||||
- **Нарезка capability и что из неё переехало в спеки:** режем по домену
|
||||
(`ingest`, `storage`, `read-api`, `mcp`), а не по транспорту. В актуальные
|
||||
спеки перенесены `ingest` и `storage`; `read-api` описан только в
|
||||
`docs/architecture.md`, `mcp` — пока только в коде. Пробел в спеке по этим двум
|
||||
темам — не находка, а известное состояние.
|
||||
- Конвенции прозой: `docs/conventions.md` *(в другом проекте это каталог из
|
||||
нескольких файлов — тогда перечисляют все:
|
||||
`docs/conventions/{logging,errors,config,database,web-ui}.md`)*. Механизировано
|
||||
и потому **не проверяется проходом по конвенциям**: форма логов,
|
||||
`fmt.Print*`/`os.Getenv`/`time.Now` мимо единых точек, сравнение ошибок,
|
||||
сторонние пакеты ошибок — всё это правила в `.golangci.yml`.
|
||||
- Архитектура и решения: `docs/architecture.md`
|
||||
- **Наблюдения на живых данных:** `docs/local-research.md` — что реально шлёт
|
||||
источник и чем это расходится с его документацией. *(Не ведём — так и пишут:
|
||||
«наблюдений на живых данных не ведём».)*
|
||||
- Журнал проскочивших дефектов: `docs/review-journal.md`
|
||||
- **Единые точки:** идентификаторы — `internal/ident`; время — `store.Now()`;
|
||||
разбор дат входного формата — один парсер в `internal/parse`; маппинг доменной
|
||||
@@ -159,6 +206,53 @@
|
||||
- **Клиент внешнего сервиса** — таймаут, протяжка `context`, поведение при
|
||||
«медленно» против «упало», ретраи и их граница.
|
||||
|
||||
## Прецеденты
|
||||
|
||||
*Воспроизведённые случаи этого проекта: класс — симптом — чем воспроизведён —
|
||||
чем закончилось. Прецедентов нет — так и пишут: «прецедентов не накоплено».*
|
||||
|
||||
- **Вырожденный ответ библиотеки, неотличимый от штатного.** Симптом: пересборка
|
||||
докладывала «журнал разобран целиком», а часть записей не доезжала. Причина:
|
||||
контрольная точка журнала СУБД под занятой блокировкой возвращала `-1` вместо
|
||||
пары чисел, и сравнение `-1 >= -1` читалось как успех — 1492 тика из 5502.
|
||||
Воспроизведено экспериментом на стенде (`tmp/probe-checkpoint/`), из
|
||||
документации драйвера не следовало. Закончилось: явная проверка вырожденного
|
||||
значения + вопрос 8 в эксплуатационном проходе.
|
||||
- **Канонизация внутри транзакции.** Симптом: соседняя доставка получала «база
|
||||
занята». Причина: пересборка держала блокировку записи 5.019 с при таймауте
|
||||
занятости 5000 мс — канонизация и хеширование шли внутри транзакции.
|
||||
Воспроизведено замером на копии БД. Закончилось: вынос канонизации из
|
||||
транзакции; числа — в раздел `## Прод и поток`.
|
||||
- **Пик памяти на распаковке.** Симптом: контейнер убивался по памяти на крупных
|
||||
доставках. Причина: сжатая запись распаковывалась целиком, пик 768 МиБ на теле
|
||||
40 МБ. Воспроизведено прогоном на реальном пакете из `testdata`. Закончилось:
|
||||
потоковая обработка; факт «запись — сжатый BLOB» вынесен в бриф, потому что без
|
||||
него замер не читается как аномалия.
|
||||
|
||||
## Типовые ложноположительные
|
||||
|
||||
*Находки, которые здесь выглядят убедительно и всегда неверны. Пусто — так и
|
||||
пишут.*
|
||||
|
||||
- «Значения из входа надо нормализовать перед записью» — инвариант требует
|
||||
дословного хранения; нормализация тут порча, а не улучшение.
|
||||
- «Приём должен отвечать ошибкой на непонятое содержимое» — инвариант «сохранили
|
||||
— значит приняли»; отправитель доставку не повторит.
|
||||
- «Порядок ключей в JSON стабилен, канонизация избыточна» — наблюдение на живых
|
||||
данных говорит обратное.
|
||||
- «Вынести в конфиг» про значения, заданные внешним форматом.
|
||||
|
||||
## Вопросы к проходам
|
||||
|
||||
*Производные от журнала: вопрос конкретному проходу плюс ссылка на запись, из
|
||||
которой он взялся. Пусто — так и пишут.*
|
||||
|
||||
- `ops`: что произойдёт при откате бинаря поверх уже накатившейся миграции —
|
||||
стартует ли старая версия молча (журнал, запись 2026-05-12).
|
||||
- `adversary`: имена файлов внутри архива внешнего экспорта мы не формировали —
|
||||
проверь путь от имени в архиве до операции с файловой системой (журнал, запись
|
||||
2026-06-03).
|
||||
|
||||
## Триггеры
|
||||
|
||||
- `deep`: миграция в `internal/store/migrations/`, новый пакет `internal/*`,
|
||||
@@ -170,6 +264,10 @@
|
||||
|
||||
## Недоступно проверке
|
||||
|
||||
### Не проверит ни один проход
|
||||
|
||||
*Принципиальные границы. По факту промаха не пересматриваются.*
|
||||
|
||||
- Поведение внешнего приложения-источника на следующем его обновлении.
|
||||
- Что реально лежит в системе-источнике: сверить можно только ручным экспортом,
|
||||
а он делается раз в 2–3 месяца.
|
||||
@@ -177,3 +275,15 @@
|
||||
нагрузки.
|
||||
- Завязка внешних потребителей на текущую форму ответа.
|
||||
- Суждение «этой функциональности не должно существовать».
|
||||
|
||||
### Перестали проверять сознательно
|
||||
|
||||
*Что, когда, почему и где записано. Пересматривается первым, как только что-то
|
||||
проскочило. Пусто — так и пишут: «сознательно ничего не отключали».*
|
||||
|
||||
- **Поимённая сверка со стайлгайдами языка** — с 2026-05, вместе с упразднением
|
||||
прохода про идиоматичность (журнал ревью, запись 2026-05-04). Класс обратимый:
|
||||
портит форму кода, не данные.
|
||||
- **Правило линтера про длину функции** — снято 2026-06-18: ложных срабатываний
|
||||
больше трети (журнал, там же). Вернуть, если проскочит дефект «функция делает
|
||||
три вещи».
|
||||
|
||||
@@ -18,21 +18,28 @@
|
||||
передавать каждому проходу;
|
||||
2. `docs/review-brief.md`;
|
||||
3. `.claude/review-brief.md`;
|
||||
4. брифа нет — **деградированный режим** (см. ниже).
|
||||
4. брифа нет ни по одному пути — **он заводится**, скиллом
|
||||
`av-dev-pipeline:project-brief`, и прогон продолжается по заведённому.
|
||||
|
||||
Разрешает путь конвейер, один раз, и дальше передаёт готовым. Агент, получивший
|
||||
путь в задании, сам ничего не ищет.
|
||||
|
||||
## Деградированный режим
|
||||
## Деградированный режим — исход, а не умолчание
|
||||
|
||||
Брифа нет — проходы работают, но их recall падает предсказуемым образом, и это
|
||||
**обязано быть названо**, а не сглажено. Каждый проход без брифа:
|
||||
Он включается ровно тогда, когда бриф **завести не удалось**: репозиторий
|
||||
доступен только на чтение, человек прямо запретил, инварианты вывести неоткуда.
|
||||
Во всех остальных случаях брифа быть обязано.
|
||||
|
||||
Каждый проход в этом режиме:
|
||||
|
||||
- не присваивает `critical` по основанию «нарушен инвариант проекта» — инвариантов
|
||||
он не знает;
|
||||
- не оперирует числами объёма и потока — формулирует условиями;
|
||||
- пишет в границы покрытия строку: «брифа проекта нет: инварианты, модель угроз и
|
||||
профиль нагрузки неизвестны; находки этих классов не искались».
|
||||
- пишет в границы покрытия строку: «брифа проекта нет (<причина>): инварианты,
|
||||
модель угроз и профиль нагрузки неизвестны; находки этих классов не искались».
|
||||
|
||||
Причина обязательна: без неё строка неотличима от «мы просто не стали», и
|
||||
одинаковая строка в каждом отчёте перестаёт читаться на третьей задаче.
|
||||
|
||||
Триаж сводит эти строки в одну и выносит в финальный отчёт. Отсутствие брифа —
|
||||
дыра покрытия, а не нейтральное умолчание.
|
||||
@@ -67,6 +74,14 @@ Markdown. Разделы — заголовки второго уровня с *
|
||||
Это единственный раздел, который **цитируется формулировкой**, а не пересказывается
|
||||
ссылкой: по нему присваивается severity, и пересказ здесь стоит неверной оценки.
|
||||
|
||||
**Оговорка про severity, потому что она единственная не цитируется.** Проекты
|
||||
почти никогда не пишут severity рядом с инвариантом — её приходится выводить, и
|
||||
правило вывода одно: **по обратимости последствия**. Необратимо и молча —
|
||||
`critical`; лечится повтором, видно сразу — ниже. Выведенная severity помечается
|
||||
словом «выведена по обратимости», а не выдаётся за решение проекта: проход ставит
|
||||
по ней `critical`, и он вправе знать, чьё это суждение. Лучший исход — дописать
|
||||
severity туда, откуда цитируется формулировка, и тогда пометка снимается.
|
||||
|
||||
Читают: `specs` (режим 1 — отражены ли задетые инварианты в спеке), `code`,
|
||||
`adversary`, `architecture`, `triage` (ранжирование и разметка «развилка»).
|
||||
|
||||
@@ -96,6 +111,14 @@ Markdown. Разделы — заголовки второго уровня с *
|
||||
- **запуск изменения вживую** — чем поднять и как проверить поведение (нужно
|
||||
пайплайну задачи на шаге поведенческой верификации);
|
||||
- **тесты, линт, дополнительные проверки** — и какие из них дорогие;
|
||||
- **дорогие проверки вне гейта — с адресатом.** Мало сказать «`verify:archive`
|
||||
идёт минуту»: назови, **кто и когда обязан** её гонять — какой класс изменения
|
||||
её требует, кто её запускает (проход, пайплайн, человек) и что делать, если она
|
||||
не прогонялась. Без адресата дорогая проверка не гоняется никогда, а её
|
||||
краснота не видна никому. **Адресат в проекте не записан нигде — тогда бриф его
|
||||
назначает**, и назначение помечается: «адресат назначен брифом, владельцем не
|
||||
подтверждён». Это тот же класс, что выведенная severity у инварианта: слот
|
||||
честнее заполнить назначением с пометкой, чем оставить пустым;
|
||||
- **что запускать запрещено**: рабочая БД, боевой каталог данных, внешние
|
||||
сервисы. Формулируй запретом с путями, а не «будь осторожен».
|
||||
|
||||
@@ -103,18 +126,48 @@ Markdown. Разделы — заголовки второго уровня с *
|
||||
|
||||
### `## Прод и поток` — обязателен
|
||||
|
||||
Материал для эксплуатационного прохода, и он же — половина ранжирования триажа:
|
||||
Материал для эксплуатационного прохода, и он же — половина ранжирования триажа.
|
||||
|
||||
**Первой строкой — главный вопрос эксплуатации этого проекта.** Один заголовок
|
||||
покрывает противоположные постановки: «поток идёт непрерывно и молча, отправитель
|
||||
об отказе не узнает» и «мы опрашиваем чужие сервисы, и главный вопрос — что
|
||||
делать, когда сосед отвечает медленно». От того, какая из них здесь главная,
|
||||
зависит порядок находок в отчёте, а вывести её проход не может — он видит
|
||||
одинаковый код.
|
||||
|
||||
Дальше:
|
||||
|
||||
- где это работает: машина, окружение, что рядом, кто перезапускает;
|
||||
- **внешние зависимости поимённо** и чем каждая отказывает: не только «падает», но
|
||||
и «отвечает медленно», «молчит», «отдаёт мусор». Эксплуатационный проход
|
||||
спрашивает про каждую отдельно, и список зависимостей он взять больше неоткуда;
|
||||
спрашивает про каждую отдельно, и список зависимостей он взять больше неоткуда.
|
||||
**Зависимостей почти нет — так и напиши**: «внешних зависимостей нет, смотри на
|
||||
диск и на СУБД». Пустой пункт, не названный пустым, проход тратит впустую или
|
||||
заполняет выдумкой;
|
||||
- **кто заметит отказ и когда** — есть ли вообще наблюдатель;
|
||||
- **характер потока**: непрерывный и молчаливый, по запросу, по расписанию; есть
|
||||
ли обратная связь у отправителя;
|
||||
- **представление данных и настройки хранилища.** Чем физически лежит запись
|
||||
(сжатый BLOB, JSON-строка, колонки), что происходит при чтении и записи
|
||||
(распаковка целиком, read-modify-write), и **настройки, у которых есть
|
||||
числовое значение**: таймаут занятости СУБД, режим журналирования, лимит тела,
|
||||
размер пула, ретеншен. Это не украшение раздела: ровно эти два факта
|
||||
превращают **замер** в находку. Замеренный пик памяти — аномалия только если
|
||||
известно, что запись лежит сжатой и распаковывается целиком; замеренная
|
||||
длительность удержания блокировки — гарантированный отказ соседа только если
|
||||
известно, чему равен таймаут занятости. Без этих фактов проход снимет верное
|
||||
число и честно понизит находку до гипотезы, потому что сравнить его будет не с
|
||||
чем. Цена пропущенного пункта здесь не «не найдём», а **«найдём и не
|
||||
починим»**. Числа с провенансом — в следующем пункте, воспроизведённые случаи —
|
||||
в разделе `## Прецеденты`;
|
||||
- **измеренные числа с провенансом**: объёмы, размеры тел, темп, размеры таблиц.
|
||||
Число без источника проход обязан превратить в условие — так и напиши, откуда
|
||||
оно;
|
||||
оно. **Замер и настройка — разные пункты, и путать их нельзя:** настройка
|
||||
(`busy_timeout`, лимит тела, размер пула) живёт пунктом выше и говорит, чему
|
||||
равен порог; замер говорит, что происходит на самом деле. Проекту без
|
||||
наблюдаемой нагрузки нечего писать во втором пункте — **так и напиши**:
|
||||
«измеренных чисел нагрузки нет, всё, что ниже, — настройки». Тогда проход
|
||||
формулирует условиями осознанно, а не потому, что не нашёл;
|
||||
- **что обратимо, а что нет.** Падение, которое лечится повтором, и тихая потеря,
|
||||
которую нечем восстановить, — разные классы, и порядок находок в отчёте зависит
|
||||
от того, какой из них здесь главный.
|
||||
@@ -123,6 +176,21 @@ Markdown. Разделы — заголовки второго уровня с *
|
||||
|
||||
### `## Модель угроз` — обязателен
|
||||
|
||||
**Первой строкой — периметр.** «Сервис открыт наружу; злоумышленник в локальной
|
||||
сети неинтересен» и «контур доверенный, публичного интернета здесь нет, не
|
||||
выдумывай его» — это один и тот же заголовок при противоположной постановке, и
|
||||
враждебный проход не может выбрать между ними сам. Периметр, объявленный первой
|
||||
строкой, задаёт смысл всему остальному разделу.
|
||||
|
||||
**Периметров может быть два — целевой и сегодняшний**, если контур ещё не
|
||||
развёрнут: «целевой — открыт наружу за прокси с TLS; сегодняшний — только
|
||||
локальная машина, токены пусты осознанно». Тогда назови оба и скажи прямо,
|
||||
**против какого строятся находки**. Иначе враждебный проход либо завалит отчёт
|
||||
находками «нет TLS» по сегодняшнему состоянию, либо не станет искать дефекты,
|
||||
спящие до выкладки, — оба исхода стоят прохода целиком.
|
||||
|
||||
Дальше:
|
||||
|
||||
- **что недоверенное** и каким каналом приходит: тело запроса, файл, аргумент
|
||||
команды, ответ внешней системы, содержимое архива;
|
||||
- **из чего строятся пути и ключи** — раскладка файлов на диске, состав
|
||||
@@ -139,9 +207,32 @@ Markdown. Разделы — заголовки второго уровня с *
|
||||
|
||||
Где что лежит, путями:
|
||||
|
||||
- **основная ветка** — её имя. Отсюда берутся ветки задач, в неё вливается батч,
|
||||
от неё считается база диффа по умолчанию (`git merge-base HEAD <основная>`).
|
||||
Батч подставляет это имя в каждую команду git; взять его больше неоткуда, а
|
||||
угадывание между `master` и `main` ломает интеграцию целиком;
|
||||
- актуальные спеки и дельта-спеки предлагаемого изменения;
|
||||
- конвенции прозой — и **какая их часть уже механизирована** правилом (её проход
|
||||
по конвенциям не проверяет);
|
||||
- **нарезка capability и миграционное состояние спек** — по какому признаку
|
||||
проект режет capability (по домену, по транспорту, по подсистеме), какие из них
|
||||
уже перенесены в актуальные спеки, а какие ещё живут только в документации или
|
||||
в коде. Проход по спекам иначе примет непереехавшую тему за пробел в спеке, а
|
||||
архитектурный — за отсутствие понятия;
|
||||
- конвенции прозой — **файл или каталог файлов**, путями; и **какая их часть уже
|
||||
механизирована** правилом. Механизация бывает **в нескольких местах сразу**:
|
||||
конфиг линтера, собственный анализатор и — чаще всего незамеченное —
|
||||
**тест-сканер исходников** (правило про направление зависимостей, форму
|
||||
миграций, логику в транспорте), который внешне неотличим от обычного теста.
|
||||
Перечисли все места: непойманное место механизации означает, что проход по
|
||||
конвенциям будет добросовестно проверять уже проверенное;
|
||||
- **наблюдения на живых данных** — где записано, как внешний мир ведёт себя на
|
||||
самом деле (что реально шлёт источник, чем документация формата расходится с
|
||||
практикой, какие числа сняты с живого потока). Их спрашивают `specs`, `reimpl`
|
||||
и `ops`, и все трое — «из раздела `## Карта`». Отдельного файла нет — **так и
|
||||
напиши**, и перечисли суррогаты: спеки, где наблюдения рассыпаны, комментарии в
|
||||
адаптерах, `testdata`. Отдельный файл — лучшая форма, потому что при нескольких
|
||||
внешних источниках наблюдения иначе не сойдутся в одном месте; но честный
|
||||
перечень суррогатов лучше молчания, от которого три прохода ищут
|
||||
несуществующий путь;
|
||||
- архитектура и решения; журнал проскочивших дефектов;
|
||||
- **единые точки проекта** — где генерируются идентификаторы и время, где
|
||||
единственный парсер входного формата, где маппинг доменной ошибки в код ответа,
|
||||
@@ -161,10 +252,83 @@ Markdown. Разделы — заголовки второго уровня с *
|
||||
репозиторий, воркер, клиент внешнего API, CLI-команда, файловое хранилище), и по
|
||||
3–5 **специфичных для рода** проверяемых свойств к каждому.
|
||||
|
||||
**Рода, а не инвентарь того, что сейчас лежит в пакетах.** Список пишется по
|
||||
природе проекта: род, который проект уже задумал, но ещё не написал, включать
|
||||
полезно (рубрика на него понадобится ровно на той задаче, где его заводят); а
|
||||
род, случайно оказавшийся в коде в одном экземпляре, — нет. Иначе раздел
|
||||
протухает на каждой задаче и требует пересмотра, которого никто не делает.
|
||||
|
||||
Читает: `rubric`. Без раздела рубрика выродится в общие слова и повторит
|
||||
конвенции — то есть станет applicative-проходом, ради отсутствия которого она и
|
||||
существует.
|
||||
|
||||
### `## Прецеденты` — обязателен, хотя бы строкой «пусто»
|
||||
|
||||
**Воспроизведённые случаи этого проекта, с оракулом.** Не «здесь бывают гонки», а
|
||||
«такой дефект здесь уже был, вот чем он воспроизведён»: что оказалось не так,
|
||||
каким экспериментом или тестом это показано, какими числами, где это записано.
|
||||
|
||||
Каждый пункт — четыре вещи:
|
||||
|
||||
- **класс дефекта** — так, чтобы проход узнал его в другом месте;
|
||||
- **как проявился** — симптом, который увидел человек;
|
||||
- **чем воспроизведён** — команда, тест, стенд, замер. Без этого пункт
|
||||
превращается в байку. **Регрессионный тест, написанный вместе с починкой,
|
||||
годится** наравне с независимым экспериментом: он исполняемый и падает на
|
||||
старом коде, а это всё, что требуется от оракула. Слабее он ровно в одном —
|
||||
сформулирован уже зная ответ; это отмечается словом, а не служит поводом
|
||||
выбросить пункт;
|
||||
- **чем закончилось** — починка, правило линтера, пункт брифа, «ничего».
|
||||
|
||||
Зачем раздел существует. Прецедент — самая сильная опора, какая у прохода вообще
|
||||
бывает: он проектный, воспроизводимый и уже однажды оказался правдой. Пока слота
|
||||
не было, прецеденты вмерзали в charter'ы проходов — то есть каждый проект читал
|
||||
про чужую контрольную точку в чужой СУБД и искал её у себя. Charter описывает
|
||||
**форму класса**, бриф — **случай**.
|
||||
|
||||
Источники: журнал проскочивших дефектов, архивные отчёты триажа, `git log` по
|
||||
починкам. Прецедентов нет — так и напиши: «прецедентов не накоплено», и это
|
||||
честнее пустого раздела.
|
||||
|
||||
Читают: все проходы — свой класс; `triage` — как готовый оракул.
|
||||
|
||||
### `## Типовые ложноположительные` — необязателен, но без него отсев слепой
|
||||
|
||||
Находки, которые в **этом** проекте выглядят убедительно и всегда неверны. Это
|
||||
единственный проектный вход в шаг триажа «отсев вкусовщины»: общие критерии
|
||||
(«не меняет поведения, не влияет на стоимость следующего изменения, не нарушает
|
||||
записанного») ловят вкусовщину, но не ловят находку, которая нарушает общее
|
||||
правило **осознанно**.
|
||||
|
||||
Каждый пункт — формулировка находки, какой её выдаёт проход, плюс одна строка
|
||||
«почему здесь это не дефект». Типичные обитатели: «дословное хранение надо
|
||||
нормализовать» там, где дословность — инвариант; «повтор надо сделать
|
||||
идемпотентным» там, где повтор невозможен по построению; «это надо вынести в
|
||||
конфиг» там, где значение задано внешним протоколом.
|
||||
|
||||
Читает: `triage`.
|
||||
|
||||
### `## Вопросы к проходам` — необязателен
|
||||
|
||||
Проектные вопросы, адресованные **поимённо** конкретному проходу. Главный их
|
||||
источник — журнал проскочивших дефектов: запись «проход не мог этого знать» чаще
|
||||
всего лечится фактом в другом разделе, но иногда лечится не фактом, а
|
||||
**вопросом**: «`ops`, спроси про поведение при откате бинаря поверх новой схемы»,
|
||||
«`adversary`, проверь имена внутри архива». Такие вопросы живут здесь, а не в
|
||||
charter'е: charter общий для всех проектов, а вопрос выведен из промаха в этом.
|
||||
|
||||
**Журнал — не единственный источник, а лучший.** У молодого проекта журнал пуст,
|
||||
и слот тогда заполняется из того, что есть: незакрытые находки аудита, известное
|
||||
расхождение кода с документацией, место, где решение принято «пока так». Правило
|
||||
одно и не смягчается — **у каждого вопроса указан провенанс**, и по нему видно,
|
||||
насколько он выстрадан: «журнал, запись такая-то» весит больше, чем «открытая
|
||||
находка аудита».
|
||||
|
||||
Форма: `<имя прохода>: <вопрос> (<провенанс>)`. Проход, увидев свой блок, задаёт
|
||||
эти вопросы **дополнительно** к обязательным — и отвечает на них в выводе явно.
|
||||
|
||||
Читают: проходы, названные поимённо.
|
||||
|
||||
### `## Триггеры` — необязателен
|
||||
|
||||
Проектная конкретизация правила выбора профиля: какие пути и контракты означают
|
||||
@@ -175,16 +339,35 @@ Markdown. Разделы — заголовки второго уровня с *
|
||||
|
||||
Читают: скилл конвейера, пайплайн задачи.
|
||||
|
||||
### `## Недоступно проверке` — обязателен
|
||||
### `## Недоступно проверке` — обязателен, и делится на два подраздела
|
||||
|
||||
Что не проверит ни один проход и почему: поведение внешних систем и их будущих
|
||||
версий, реальный профиль нагрузки, соответствие сохранённого действительности,
|
||||
завязка внешних потребителей на текущую форму, суждение «а нужна ли эта
|
||||
функциональность».
|
||||
Раздел целиком уезжает в границы покрытия финального отчёта — он существует ровно
|
||||
затем, чтобы «критичных проблем не обнаружено» никогда не читалось как «проверено
|
||||
всё». Но внутри лежат **два разных класса**, и смешивать их нельзя: при следующем
|
||||
промахе один пересматривается, другой нет.
|
||||
|
||||
Этот раздел целиком уезжает в границы покрытия финального отчёта. Он существует
|
||||
ровно затем, чтобы «критичных проблем не обнаружено» никогда не читалось как
|
||||
«проверено всё».
|
||||
#### `### Не проверит ни один проход`
|
||||
|
||||
Принципиально недоступное: поведение внешних систем и их будущих версий, реальный
|
||||
профиль нагрузки, соответствие сохранённого действительности, завязка внешних
|
||||
потребителей на текущую форму, суждение «а нужна ли эта функциональность».
|
||||
|
||||
Этот список не пересматривается по факту промаха: дефект отсюда — не ошибка
|
||||
конвейера, а его честная граница. Он меняется только когда меняется сам проект
|
||||
(появился стенд, появился второй потребитель, появилась телеметрия).
|
||||
|
||||
#### `### Перестали проверять сознательно`
|
||||
|
||||
Решения о сужении: перестали звать проход, понизили профиль правилом, сузили
|
||||
класс проверяемого, сняли правило линтера как шумное. Каждый пункт — **что
|
||||
перестали, когда и почему**, со ссылкой на запись журнала ревью.
|
||||
|
||||
Этот список **пересматривается первым**, как только что-то проскочило: первый
|
||||
вопрос по любому пропущенному дефекту — «не тот ли это класс, который мы перестали
|
||||
проверять». Пункт, из-за которого дефект проскочил, либо возвращается, либо
|
||||
получает строку «оставляем, цена поимки выше цены дефекта» с датой.
|
||||
|
||||
Оба подраздела обязательны; пустой называется пустым.
|
||||
|
||||
Читает: `triage`; каждый проход — свою часть.
|
||||
|
||||
@@ -194,15 +377,37 @@ Markdown. Разделы — заголовки второго уровня с *
|
||||
или в архитектуре, попадает сюда ссылкой и одной строкой сути. Два дома для
|
||||
одного факта разъезжаются, и разошедшийся бриф хуже отсутствующего: он выглядит
|
||||
актуальным. Исключение одно — раздел инвариантов, он цитируется.
|
||||
- **Числа — с провенансом.** «Тела доходили до 42 МБ (замер, ссылка)». Число без
|
||||
источника проход не имеет права использовать как утверждение.
|
||||
- **Числа — с провенансом, и провенанс проверяется переходом по ссылке.** «Тела
|
||||
доходили до 42 МБ (замер, ссылка)». Число без источника проход не имеет права
|
||||
использовать как утверждение. Отдельный и более коварный случай — **число, чей
|
||||
источник по ссылке не подтверждается**: в документе по ссылке другое число, или
|
||||
его там нет вовсе. Такое число не выбрасывается и не переписывается по догадке:
|
||||
оно остаётся с пометкой «расходится с источником: там <что нашли>», а проход
|
||||
обязан читать его как условие, а не как замер. Молча подставить «правильное»
|
||||
число хуже всего — расхождение перестанет быть видно, а причина его останется.
|
||||
- **Пустой пункт называется пустым.** «Внешних зависимостей нет — смотри на диск
|
||||
и на СУБД», «прецедентов не накоплено», «наблюдений на живых данных не ведём»,
|
||||
«измеренных чисел нагрузки нет», «сознательно ничего не отключали». Отсутствие
|
||||
строки читается проходом как «здесь не написали», и он тратит обязательный
|
||||
вопрос впустую либо заполняет пробел выдумкой. Прямое «пусто» стоит одной
|
||||
строки и экономит проход целиком.
|
||||
- **Назначенное помечается назначенным.** Бриф отражает решения проекта, но
|
||||
местами оказывается **первым** местом, где решение вообще записано: severity у
|
||||
инварианта, адресат дорогой проверки, периметр, восстановленный из конфига.
|
||||
Так можно — молчать хуже, — но пометка обязательна («выведена по обратимости»,
|
||||
«назначен брифом, владельцем не подтверждён»). Проход имеет право знать, чьё
|
||||
это суждение, а владелец — увидеть, что за него что-то решили.
|
||||
- **Что вне модели — называется явно.** Это относится и к угрозам, и к нагрузке,
|
||||
и к классам находок, которые проект сознательно перестал проверять.
|
||||
и к классам находок, которые проект сознательно перестал проверять (последние —
|
||||
в свой подраздел `## Недоступно проверке`, а не вперемешку с принципиальным).
|
||||
- **Бриф подчиняется промоуту.** Свойство, ставшее правилом линтера, из брифа
|
||||
вычёркивается — как и из конвенций, и из charter'ов (см.
|
||||
[promote.md](promote.md), шаг 3).
|
||||
- **Когда обновлять:** сменился гейт; появился новый контур, зависимость или
|
||||
источник входа; журнал ревью получил запись вида «проход не мог этого знать».
|
||||
Планового пересмотра нет.
|
||||
- **Бриф ведёт проект**, а не плагин. Плагин его только читает и никогда не
|
||||
правит.
|
||||
источник входа; журнал ревью получил запись вида «проход не мог этого знать»;
|
||||
воспроизвели дефект — он идёт в `## Прецеденты`. Планового пересмотра нет.
|
||||
- **Заводится и обновляется шагом, а не руками** — скиллом
|
||||
`av-dev-pipeline:project-brief`. Он же вызывается автоматически, когда конвейер
|
||||
или пайплайн задачи не нашли брифа ни по одному пути.
|
||||
- **Бриф ведёт проект**, а не плагин. Файл живёт в репозитории проекта; плагин
|
||||
его читает и заводит по шаблону, но не хранит у себя и не подменяет.
|
||||
|
||||
@@ -23,7 +23,8 @@
|
||||
калибровки: проход, чьи находки регулярно доезжают до конвенции, оправдан;
|
||||
проход, чьи находки не доезжают никогда, — кандидат на `drop` (см.
|
||||
[calibration.md](calibration.md)).
|
||||
- Место записи — файл конвенций проекта (путь — в разделе `## Карта` брифа). Если
|
||||
- Место записи — конвенции проекта, файл или нужный файл каталога (путь — в
|
||||
разделе `## Карта` брифа). Если
|
||||
тема относится к поведению системы, а не к тому, как мы пишем код, — это не
|
||||
конвенция, а требование: заводится дельта-спека обычным путём.
|
||||
|
||||
|
||||
@@ -19,6 +19,11 @@
|
||||
а потому, что здесь лежит цена: если что-то теперь проскочит, первый вопрос —
|
||||
«не тот ли это класс, который мы перестали проверять».
|
||||
|
||||
Каждое такое решение обязано получить **строку в брифе** — в подразделе
|
||||
`### Перестали проверять сознательно` раздела `## Недоступно проверке`. Журнал
|
||||
хранит «почему тогда так решили», бриф — то, во что смотрит каждый прогон.
|
||||
Решение, оставшееся только в журнале, в границы покрытия не доедет.
|
||||
|
||||
## Форма записи
|
||||
|
||||
```
|
||||
@@ -41,7 +46,11 @@
|
||||
|
||||
- **в бриф проекта** — если проход не мог знать факта: объём, характер потока,
|
||||
что здесь необратимо, какой шаг гейта красит безусловно. Самый частый адрес и
|
||||
самый дешёвый.
|
||||
самый дешёвый. Сюда же — **воспроизведённый случай** (раздел `## Прецеденты`:
|
||||
класс, симптом, чем воспроизведён, чем закончилось) и **вопрос конкретному
|
||||
проходу**, если промах лечится не фактом, а заданным вопросом (раздел
|
||||
`## Вопросы к проходам`). Прежде чем править charter, проверь, не хватит ли
|
||||
этих двух разделов: charter общий для всех проектов, бриф — про этот.
|
||||
- **в конвенции или в правило линтера** — если свойство выражается
|
||||
детерминированно (процедура — [promote.md](promote.md)).
|
||||
- **в charter прохода** — если сломан **метод**, а не знание. Правка charter'а
|
||||
|
||||
Reference in New Issue
Block a user