- скилл project-brief: бриф собирается из CLAUDE.md, архитектуры, Taskfile и конвенций и показывается человеку. Раньше единственная инструкция по его созданию лежала внутри шаблона, поэтому деградированный режим был не аварийным, а единственным: critical по основанию «нарушен инвариант» недостижим ни на одной задаче - rebase перенесён внутрь worktree задачи: прежняя форма падала на занятой ветке, и агент уводил весь батч в провалившиеся с ложной причиной - контракт брифа дополнен восемью слотами; проверен заполнением на обоих проектах, незаполнимых нет. Прецедент healthlog вынут из общего charter'а в бриф — там он вмёрз вместе с числами - шов: пайплайн задачу не закрывает и записи учёта не трогает, урожай отдаёт списком, правило остатка — ссылкой на av-dev-tasks - деградированный абзац во всех девяти проходах, вопрос 9 в ops, пространство имён в вызовах, раздел предпосылок
414 lines
38 KiB
Markdown
414 lines
38 KiB
Markdown
# Бриф проекта — контракт
|
||
|
||
Конвейер общий, а находки — проектные. Проход, не знающий, что в этом проекте
|
||
нельзя нарушать, чем краснеет гейт и сколько данных реально проходит через узел,
|
||
выдаёт правдоподобные общие места: их дорого опровергать и нечем подтверждать.
|
||
|
||
Поэтому проектная специфика живёт **в одном файле проекта**, а не в charter'ах
|
||
агентов. Charter описывает **метод** прохода (что он делает и почему именно так),
|
||
бриф — **предмет** (что здесь дорого, чем это меряется, где лежит).
|
||
|
||
Шаблон для заполнения — [brief-template.md](brief-template.md).
|
||
|
||
## Где лежит и как находится
|
||
|
||
Порядок разрешения пути, одинаковый для скилла и для каждого агента:
|
||
|
||
1. путь, названный в задании конвейера (`бриф: <путь>`) — конвейер обязан его
|
||
передавать каждому проходу;
|
||
2. `docs/review-brief.md`;
|
||
3. `.claude/review-brief.md`;
|
||
4. брифа нет ни по одному пути — **он заводится**, скиллом
|
||
`av-dev-pipeline:project-brief`, и прогон продолжается по заведённому.
|
||
|
||
Разрешает путь конвейер, один раз, и дальше передаёт готовым. Агент, получивший
|
||
путь в задании, сам ничего не ищет.
|
||
|
||
## Деградированный режим — исход, а не умолчание
|
||
|
||
Он включается ровно тогда, когда бриф **завести не удалось**: репозиторий
|
||
доступен только на чтение, человек прямо запретил, инварианты вывести неоткуда.
|
||
Во всех остальных случаях брифа быть обязано.
|
||
|
||
Каждый проход в этом режиме:
|
||
|
||
- не присваивает `critical` по основанию «нарушен инвариант проекта» — инвариантов
|
||
он не знает;
|
||
- не оперирует числами объёма и потока — формулирует условиями;
|
||
- пишет в границы покрытия строку: «брифа проекта нет (<причина>): инварианты,
|
||
модель угроз и профиль нагрузки неизвестны; находки этих классов не искались».
|
||
|
||
Причина обязательна: без неё строка неотличима от «мы просто не стали», и
|
||
одинаковая строка в каждом отчёте перестаёт читаться на третьей задаче.
|
||
|
||
Триаж сводит эти строки в одну и выносит в финальный отчёт. Отсутствие брифа —
|
||
дыра покрытия, а не нейтральное умолчание.
|
||
|
||
## Форма
|
||
|
||
Markdown. Разделы — заголовки второго уровня с **точными именами** из списка
|
||
ниже: по ним агенты находят свой кусок. Порядок разделов свободен, лишние разделы
|
||
допустимы и игнорируются, отсутствующий раздел работает как деградированный режим
|
||
для тех проходов, которые его читают.
|
||
|
||
## Разделы
|
||
|
||
### `## Проект` — обязателен
|
||
|
||
Абзац: что система делает — и, что важнее, **чего она не делает**. Граница домена
|
||
нужна архитектурному проходу как критерий: «хранилище, а не аналитика», «единое
|
||
ядро, тонкие транспорты», «связующий сервис, а не медиатека». Без неё перенос
|
||
понятия через границу выглядит просто новым кодом.
|
||
|
||
Читают: `architecture`, `rubric`, `reimpl`, `specs`.
|
||
|
||
### `## Инварианты` — обязателен
|
||
|
||
Список того, что нарушать нельзя. Каждый пункт — три вещи:
|
||
|
||
- формулировка **как проверяемое свойство**, а не как лозунг: «точка сохраняется
|
||
дословно: незнакомое поле не отбрасывается», а не «бережно относимся к данным»;
|
||
- **последствие нарушения** и его обратимость;
|
||
- **severity по умолчанию** — если это не `critical`, скажи прямо.
|
||
|
||
Это единственный раздел, который **цитируется формулировкой**, а не пересказывается
|
||
ссылкой: по нему присваивается severity, и пересказ здесь стоит неверной оценки.
|
||
|
||
**Оговорка про severity, потому что она единственная не цитируется.** Проекты
|
||
почти никогда не пишут severity рядом с инвариантом — её приходится выводить, и
|
||
правило вывода одно: **по обратимости последствия**. Необратимо и молча —
|
||
`critical`; лечится повтором, видно сразу — ниже. Выведенная severity помечается
|
||
словом «выведена по обратимости», а не выдаётся за решение проекта: проход ставит
|
||
по ней `critical`, и он вправе знать, чьё это суждение. Лучший исход — дописать
|
||
severity туда, откуда цитируется формулировка, и тогда пометка снимается.
|
||
|
||
Читают: `specs` (режим 1 — отражены ли задетые инварианты в спеке), `code`,
|
||
`adversary`, `architecture`, `triage` (ранжирование и разметка «развилка»).
|
||
|
||
### `## Гейт` — обязателен
|
||
|
||
- **Команда** целиком, включая передачу базы диффа (`task gate BASE=<база>`), и
|
||
как база определяется по умолчанию.
|
||
- **Где логи** отдельных шагов.
|
||
- **Что означает каждый исход**: чем гейт краснеет, что предупреждает, что
|
||
пропускается по составу диффа.
|
||
- **Шаги, которые красят безусловно, и почему.** Это самая ценная часть раздела:
|
||
«данные под контролем версий», «структура конфига изменилась, а образец нет»,
|
||
«миграции не накатываются с нуля» — проход обязан знать, что здесь не бывает
|
||
«ну это мелочь».
|
||
- **Чего в гейте намеренно нет** и почему — прогон на живом корпусе, длинный
|
||
интеграционный тест. У проверки, которую гейт не гоняет, краснота никому не
|
||
видна; это уезжает в границы покрытия.
|
||
|
||
Читает: `gate`.
|
||
|
||
### `## Команды` — обязателен
|
||
|
||
Что проход имеет право выполнить и чем:
|
||
|
||
- **карта проекта для архитектуры** — команда, отдающая пакеты, граф зависимостей
|
||
и инвентарь концепций (`task review:context`);
|
||
- **запуск изменения вживую** — чем поднять и как проверить поведение (нужно
|
||
пайплайну задачи на шаге поведенческой верификации);
|
||
- **тесты, линт, дополнительные проверки** — и какие из них дорогие;
|
||
- **дорогие проверки вне гейта — с адресатом.** Мало сказать «`verify:archive`
|
||
идёт минуту»: назови, **кто и когда обязан** её гонять — какой класс изменения
|
||
её требует, кто её запускает (проход, пайплайн, человек) и что делать, если она
|
||
не прогонялась. Без адресата дорогая проверка не гоняется никогда, а её
|
||
краснота не видна никому. **Адресат в проекте не записан нигде — тогда бриф его
|
||
назначает**, и назначение помечается: «адресат назначен брифом, владельцем не
|
||
подтверждён». Это тот же класс, что выведенная severity у инварианта: слот
|
||
честнее заполнить назначением с пометкой, чем оставить пустым;
|
||
- **что запускать запрещено**: рабочая БД, боевой каталог данных, внешние
|
||
сервисы. Формулируй запретом с путями, а не «будь осторожен».
|
||
|
||
Читают: `architecture`, `gate`, `ops`, `triage`, пайплайн задачи.
|
||
|
||
### `## Прод и поток` — обязателен
|
||
|
||
Материал для эксплуатационного прохода, и он же — половина ранжирования триажа.
|
||
|
||
**Первой строкой — главный вопрос эксплуатации этого проекта.** Один заголовок
|
||
покрывает противоположные постановки: «поток идёт непрерывно и молча, отправитель
|
||
об отказе не узнает» и «мы опрашиваем чужие сервисы, и главный вопрос — что
|
||
делать, когда сосед отвечает медленно». От того, какая из них здесь главная,
|
||
зависит порядок находок в отчёте, а вывести её проход не может — он видит
|
||
одинаковый код.
|
||
|
||
Дальше:
|
||
|
||
- где это работает: машина, окружение, что рядом, кто перезапускает;
|
||
- **внешние зависимости поимённо** и чем каждая отказывает: не только «падает», но
|
||
и «отвечает медленно», «молчит», «отдаёт мусор». Эксплуатационный проход
|
||
спрашивает про каждую отдельно, и список зависимостей он взять больше неоткуда.
|
||
**Зависимостей почти нет — так и напиши**: «внешних зависимостей нет, смотри на
|
||
диск и на СУБД». Пустой пункт, не названный пустым, проход тратит впустую или
|
||
заполняет выдумкой;
|
||
- **кто заметит отказ и когда** — есть ли вообще наблюдатель;
|
||
- **характер потока**: непрерывный и молчаливый, по запросу, по расписанию; есть
|
||
ли обратная связь у отправителя;
|
||
- **представление данных и настройки хранилища.** Чем физически лежит запись
|
||
(сжатый BLOB, JSON-строка, колонки), что происходит при чтении и записи
|
||
(распаковка целиком, read-modify-write), и **настройки, у которых есть
|
||
числовое значение**: таймаут занятости СУБД, режим журналирования, лимит тела,
|
||
размер пула, ретеншен. Это не украшение раздела: ровно эти два факта
|
||
превращают **замер** в находку. Замеренный пик памяти — аномалия только если
|
||
известно, что запись лежит сжатой и распаковывается целиком; замеренная
|
||
длительность удержания блокировки — гарантированный отказ соседа только если
|
||
известно, чему равен таймаут занятости. Без этих фактов проход снимет верное
|
||
число и честно понизит находку до гипотезы, потому что сравнить его будет не с
|
||
чем. Цена пропущенного пункта здесь не «не найдём», а **«найдём и не
|
||
починим»**. Числа с провенансом — в следующем пункте, воспроизведённые случаи —
|
||
в разделе `## Прецеденты`;
|
||
- **измеренные числа с провенансом**: объёмы, размеры тел, темп, размеры таблиц.
|
||
Число без источника проход обязан превратить в условие — так и напиши, откуда
|
||
оно. **Замер и настройка — разные пункты, и путать их нельзя:** настройка
|
||
(`busy_timeout`, лимит тела, размер пула) живёт пунктом выше и говорит, чему
|
||
равен порог; замер говорит, что происходит на самом деле. Проекту без
|
||
наблюдаемой нагрузки нечего писать во втором пункте — **так и напиши**:
|
||
«измеренных чисел нагрузки нет, всё, что ниже, — настройки». Тогда проход
|
||
формулирует условиями осознанно, а не потому, что не нашёл;
|
||
- **что обратимо, а что нет.** Падение, которое лечится повтором, и тихая потеря,
|
||
которую нечем восстановить, — разные классы, и порядок находок в отчёте зависит
|
||
от того, какой из них здесь главный.
|
||
|
||
Читают: `ops`, `adversary`, `triage`, `reimpl`.
|
||
|
||
### `## Модель угроз` — обязателен
|
||
|
||
**Первой строкой — периметр.** «Сервис открыт наружу; злоумышленник в локальной
|
||
сети неинтересен» и «контур доверенный, публичного интернета здесь нет, не
|
||
выдумывай его» — это один и тот же заголовок при противоположной постановке, и
|
||
враждебный проход не может выбрать между ними сам. Периметр, объявленный первой
|
||
строкой, задаёт смысл всему остальному разделу.
|
||
|
||
**Периметров может быть два — целевой и сегодняшний**, если контур ещё не
|
||
развёрнут: «целевой — открыт наружу за прокси с TLS; сегодняшний — только
|
||
локальная машина, токены пусты осознанно». Тогда назови оба и скажи прямо,
|
||
**против какого строятся находки**. Иначе враждебный проход либо завалит отчёт
|
||
находками «нет TLS» по сегодняшнему состоянию, либо не станет искать дефекты,
|
||
спящие до выкладки, — оба исхода стоят прохода целиком.
|
||
|
||
Дальше:
|
||
|
||
- **что недоверенное** и каким каналом приходит: тело запроса, файл, аргумент
|
||
команды, ответ внешней системы, содержимое архива;
|
||
- **из чего строятся пути и ключи** — раскладка файлов на диске, состав
|
||
координатного ключа записи, имя каталога. Враждебный проход выводит запись за
|
||
пределы песочницы именно отсюда, и без этого пункта он ищет вслепую;
|
||
- **что разграничивает доступ** — токены, контуры, права файлов;
|
||
- **что чувствительнее чего**: если данные дороже секретов, скажи это прямо;
|
||
- **что вне модели** — перечислить явно. Пустой пункт «вне модели» означает, что
|
||
враждебный проход выдумает угрозу сам, и находка никогда не будет исправлена.
|
||
|
||
Читает: `adversary`.
|
||
|
||
### `## Карта` — обязателен
|
||
|
||
Где что лежит, путями:
|
||
|
||
- **основная ветка** — её имя. Отсюда берутся ветки задач, в неё вливается батч,
|
||
от неё считается база диффа по умолчанию (`git merge-base HEAD <основная>`).
|
||
Батч подставляет это имя в каждую команду git; взять его больше неоткуда, а
|
||
угадывание между `master` и `main` ломает интеграцию целиком;
|
||
- актуальные спеки и дельта-спеки предлагаемого изменения;
|
||
- **нарезка capability и миграционное состояние спек** — по какому признаку
|
||
проект режет capability (по домену, по транспорту, по подсистеме), какие из них
|
||
уже перенесены в актуальные спеки, а какие ещё живут только в документации или
|
||
в коде. Проход по спекам иначе примет непереехавшую тему за пробел в спеке, а
|
||
архитектурный — за отсутствие понятия;
|
||
- конвенции прозой — **файл или каталог файлов**, путями; и **какая их часть уже
|
||
механизирована** правилом. Механизация бывает **в нескольких местах сразу**:
|
||
конфиг линтера, собственный анализатор и — чаще всего незамеченное —
|
||
**тест-сканер исходников** (правило про направление зависимостей, форму
|
||
миграций, логику в транспорте), который внешне неотличим от обычного теста.
|
||
Перечисли все места: непойманное место механизации означает, что проход по
|
||
конвенциям будет добросовестно проверять уже проверенное;
|
||
- **наблюдения на живых данных** — где записано, как внешний мир ведёт себя на
|
||
самом деле (что реально шлёт источник, чем документация формата расходится с
|
||
практикой, какие числа сняты с живого потока). Их спрашивают `specs`, `reimpl`
|
||
и `ops`, и все трое — «из раздела `## Карта`». Отдельного файла нет — **так и
|
||
напиши**, и перечисли суррогаты: спеки, где наблюдения рассыпаны, комментарии в
|
||
адаптерах, `testdata`. Отдельный файл — лучшая форма, потому что при нескольких
|
||
внешних источниках наблюдения иначе не сойдутся в одном месте; но честный
|
||
перечень суррогатов лучше молчания, от которого три прохода ищут
|
||
несуществующий путь;
|
||
- архитектура и решения; журнал проскочивших дефектов;
|
||
- **единые точки проекта** — где генерируются идентификаторы и время, где
|
||
единственный парсер входного формата, где маппинг доменной ошибки в код ответа,
|
||
где общий путь приёма. Это материал для вопроса «не появился ли второй способ»;
|
||
если команда карты проекта их выгружает, здесь хватит ссылки на неё;
|
||
- **нумерованные артефакты** — путь миграций и правило нумерации: батч раздаёт
|
||
номера заранее, чтобы параллельные задачи не столкнулись файлами;
|
||
- где ведутся задачи (пайплайн только читает и сообщает исход);
|
||
- `testdata` и что в них лежит; куда можно писать временное;
|
||
- **каталоги, которые не трогают вовсе**.
|
||
|
||
Читают: все проходы.
|
||
|
||
### `## Типовые узлы` — необязателен, но без него рубрика беднеет
|
||
|
||
Роды узлов, из которых состоит проект (парсер входного формата, HTTP-обработчик,
|
||
репозиторий, воркер, клиент внешнего API, CLI-команда, файловое хранилище), и по
|
||
3–5 **специфичных для рода** проверяемых свойств к каждому.
|
||
|
||
**Рода, а не инвентарь того, что сейчас лежит в пакетах.** Список пишется по
|
||
природе проекта: род, который проект уже задумал, но ещё не написал, включать
|
||
полезно (рубрика на него понадобится ровно на той задаче, где его заводят); а
|
||
род, случайно оказавшийся в коде в одном экземпляре, — нет. Иначе раздел
|
||
протухает на каждой задаче и требует пересмотра, которого никто не делает.
|
||
|
||
Читает: `rubric`. Без раздела рубрика выродится в общие слова и повторит
|
||
конвенции — то есть станет applicative-проходом, ради отсутствия которого она и
|
||
существует.
|
||
|
||
### `## Прецеденты` — обязателен, хотя бы строкой «пусто»
|
||
|
||
**Воспроизведённые случаи этого проекта, с оракулом.** Не «здесь бывают гонки», а
|
||
«такой дефект здесь уже был, вот чем он воспроизведён»: что оказалось не так,
|
||
каким экспериментом или тестом это показано, какими числами, где это записано.
|
||
|
||
Каждый пункт — четыре вещи:
|
||
|
||
- **класс дефекта** — так, чтобы проход узнал его в другом месте;
|
||
- **как проявился** — симптом, который увидел человек;
|
||
- **чем воспроизведён** — команда, тест, стенд, замер. Без этого пункт
|
||
превращается в байку. **Регрессионный тест, написанный вместе с починкой,
|
||
годится** наравне с независимым экспериментом: он исполняемый и падает на
|
||
старом коде, а это всё, что требуется от оракула. Слабее он ровно в одном —
|
||
сформулирован уже зная ответ; это отмечается словом, а не служит поводом
|
||
выбросить пункт;
|
||
- **чем закончилось** — починка, правило линтера, пункт брифа, «ничего».
|
||
|
||
Зачем раздел существует. Прецедент — самая сильная опора, какая у прохода вообще
|
||
бывает: он проектный, воспроизводимый и уже однажды оказался правдой. Пока слота
|
||
не было, прецеденты вмерзали в charter'ы проходов — то есть каждый проект читал
|
||
про чужую контрольную точку в чужой СУБД и искал её у себя. Charter описывает
|
||
**форму класса**, бриф — **случай**.
|
||
|
||
Источники: журнал проскочивших дефектов, архивные отчёты триажа, `git log` по
|
||
починкам. Прецедентов нет — так и напиши: «прецедентов не накоплено», и это
|
||
честнее пустого раздела.
|
||
|
||
Читают: все проходы — свой класс; `triage` — как готовый оракул.
|
||
|
||
### `## Типовые ложноположительные` — необязателен, но без него отсев слепой
|
||
|
||
Находки, которые в **этом** проекте выглядят убедительно и всегда неверны. Это
|
||
единственный проектный вход в шаг триажа «отсев вкусовщины»: общие критерии
|
||
(«не меняет поведения, не влияет на стоимость следующего изменения, не нарушает
|
||
записанного») ловят вкусовщину, но не ловят находку, которая нарушает общее
|
||
правило **осознанно**.
|
||
|
||
Каждый пункт — формулировка находки, какой её выдаёт проход, плюс одна строка
|
||
«почему здесь это не дефект». Типичные обитатели: «дословное хранение надо
|
||
нормализовать» там, где дословность — инвариант; «повтор надо сделать
|
||
идемпотентным» там, где повтор невозможен по построению; «это надо вынести в
|
||
конфиг» там, где значение задано внешним протоколом.
|
||
|
||
Читает: `triage`.
|
||
|
||
### `## Вопросы к проходам` — необязателен
|
||
|
||
Проектные вопросы, адресованные **поимённо** конкретному проходу. Главный их
|
||
источник — журнал проскочивших дефектов: запись «проход не мог этого знать» чаще
|
||
всего лечится фактом в другом разделе, но иногда лечится не фактом, а
|
||
**вопросом**: «`ops`, спроси про поведение при откате бинаря поверх новой схемы»,
|
||
«`adversary`, проверь имена внутри архива». Такие вопросы живут здесь, а не в
|
||
charter'е: charter общий для всех проектов, а вопрос выведен из промаха в этом.
|
||
|
||
**Журнал — не единственный источник, а лучший.** У молодого проекта журнал пуст,
|
||
и слот тогда заполняется из того, что есть: незакрытые находки аудита, известное
|
||
расхождение кода с документацией, место, где решение принято «пока так». Правило
|
||
одно и не смягчается — **у каждого вопроса указан провенанс**, и по нему видно,
|
||
насколько он выстрадан: «журнал, запись такая-то» весит больше, чем «открытая
|
||
находка аудита».
|
||
|
||
Форма: `<имя прохода>: <вопрос> (<провенанс>)`. Проход, увидев свой блок, задаёт
|
||
эти вопросы **дополнительно** к обязательным — и отвечает на них в выводе явно.
|
||
|
||
Читают: проходы, названные поимённо.
|
||
|
||
### `## Триггеры` — необязателен
|
||
|
||
Проектная конкретизация правила выбора профиля: какие пути и контракты означают
|
||
`deep`; что считается «поведением, видимым снаружи»; при каком изменении
|
||
запускается `reimpl`. Умолчания записаны в самом скилле и работают без этого
|
||
раздела — но общее правило говорит «изменение публичного контракта», а какой
|
||
контракт публичный, знает только проект.
|
||
|
||
Читают: скилл конвейера, пайплайн задачи.
|
||
|
||
### `## Недоступно проверке` — обязателен, и делится на два подраздела
|
||
|
||
Раздел целиком уезжает в границы покрытия финального отчёта — он существует ровно
|
||
затем, чтобы «критичных проблем не обнаружено» никогда не читалось как «проверено
|
||
всё». Но внутри лежат **два разных класса**, и смешивать их нельзя: при следующем
|
||
промахе один пересматривается, другой нет.
|
||
|
||
#### `### Не проверит ни один проход`
|
||
|
||
Принципиально недоступное: поведение внешних систем и их будущих версий, реальный
|
||
профиль нагрузки, соответствие сохранённого действительности, завязка внешних
|
||
потребителей на текущую форму, суждение «а нужна ли эта функциональность».
|
||
|
||
Этот список не пересматривается по факту промаха: дефект отсюда — не ошибка
|
||
конвейера, а его честная граница. Он меняется только когда меняется сам проект
|
||
(появился стенд, появился второй потребитель, появилась телеметрия).
|
||
|
||
#### `### Перестали проверять сознательно`
|
||
|
||
Решения о сужении: перестали звать проход, понизили профиль правилом, сузили
|
||
класс проверяемого, сняли правило линтера как шумное. Каждый пункт — **что
|
||
перестали, когда и почему**, со ссылкой на запись журнала ревью.
|
||
|
||
Этот список **пересматривается первым**, как только что-то проскочило: первый
|
||
вопрос по любому пропущенному дефекту — «не тот ли это класс, который мы перестали
|
||
проверять». Пункт, из-за которого дефект проскочил, либо возвращается, либо
|
||
получает строку «оставляем, цена поимки выше цены дефекта» с датой.
|
||
|
||
Оба подраздела обязательны; пустой называется пустым.
|
||
|
||
Читает: `triage`; каждый проход — свою часть.
|
||
|
||
## Правила ведения
|
||
|
||
- **Бриф не пересказывает документацию проекта.** Факт, записанный в `CLAUDE.md`
|
||
или в архитектуре, попадает сюда ссылкой и одной строкой сути. Два дома для
|
||
одного факта разъезжаются, и разошедшийся бриф хуже отсутствующего: он выглядит
|
||
актуальным. Исключение одно — раздел инвариантов, он цитируется.
|
||
- **Числа — с провенансом, и провенанс проверяется переходом по ссылке.** «Тела
|
||
доходили до 42 МБ (замер, ссылка)». Число без источника проход не имеет права
|
||
использовать как утверждение. Отдельный и более коварный случай — **число, чей
|
||
источник по ссылке не подтверждается**: в документе по ссылке другое число, или
|
||
его там нет вовсе. Такое число не выбрасывается и не переписывается по догадке:
|
||
оно остаётся с пометкой «расходится с источником: там <что нашли>», а проход
|
||
обязан читать его как условие, а не как замер. Молча подставить «правильное»
|
||
число хуже всего — расхождение перестанет быть видно, а причина его останется.
|
||
- **Пустой пункт называется пустым.** «Внешних зависимостей нет — смотри на диск
|
||
и на СУБД», «прецедентов не накоплено», «наблюдений на живых данных не ведём»,
|
||
«измеренных чисел нагрузки нет», «сознательно ничего не отключали». Отсутствие
|
||
строки читается проходом как «здесь не написали», и он тратит обязательный
|
||
вопрос впустую либо заполняет пробел выдумкой. Прямое «пусто» стоит одной
|
||
строки и экономит проход целиком.
|
||
- **Назначенное помечается назначенным.** Бриф отражает решения проекта, но
|
||
местами оказывается **первым** местом, где решение вообще записано: severity у
|
||
инварианта, адресат дорогой проверки, периметр, восстановленный из конфига.
|
||
Так можно — молчать хуже, — но пометка обязательна («выведена по обратимости»,
|
||
«назначен брифом, владельцем не подтверждён»). Проход имеет право знать, чьё
|
||
это суждение, а владелец — увидеть, что за него что-то решили.
|
||
- **Что вне модели — называется явно.** Это относится и к угрозам, и к нагрузке,
|
||
и к классам находок, которые проект сознательно перестал проверять (последние —
|
||
в свой подраздел `## Недоступно проверке`, а не вперемешку с принципиальным).
|
||
- **Бриф подчиняется промоуту.** Свойство, ставшее правилом линтера, из брифа
|
||
вычёркивается — как и из конвенций, и из charter'ов (см.
|
||
[promote.md](promote.md), шаг 3).
|
||
- **Когда обновлять:** сменился гейт; появился новый контур, зависимость или
|
||
источник входа; журнал ревью получил запись вида «проход не мог этого знать»;
|
||
воспроизвели дефект — он идёт в `## Прецеденты`. Планового пересмотра нет.
|
||
- **Заводится и обновляется шагом, а не руками** — скиллом
|
||
`av-dev-pipeline:project-brief`. Он же вызывается автоматически, когда конвейер
|
||
или пайплайн задачи не нашли брифа ни по одному пути.
|
||
- **Бриф ведёт проект**, а не плагин. Файл живёт в репозитории проекта; плагин
|
||
его читает и заводит по шаблону, но не хранит у себя и не подменяет.
|