Files
dev-skills/av-dev-pipeline/skills/review-pipeline/references/brief-template.md
T
av 9219f4a5cd добавлены плагины av-dev-tasks и av-dev-pipeline
Пара плагинов с намеренно проведённой границей: av-dev-tasks отвечает
за то, что делаем и в каком порядке, av-dev-pipeline — за то, как ведём
одну задачу. Зависимости между ними нет: управление задачами работает и
с ручным исполнением, пайплайн — на проекте с любым учётом задач.

- av-dev-tasks — преемник av-dev-backlog: цели вместо приоритетов,
  спринт под одну цель с заморозкой набора, различение вопроса и
  блокера, каденция «вопросы — разбор — переоценка — набор».
  Раскладка docs/tasks с items/, PLAN.md, BACKLOG.md, SPRINT.md,
  REJECTED.md; проверенное из av-dev-backlog перенесено, не переписано.
- av-dev-pipeline — вынос того, что лежало копиями в healthlog и
  jellybit (3628 строк) и уже разошлось: цикл SDD, конвейер ревью с
  обязательным триажем, прогон нескольких задач разом. Проектная
  специфика вынесена в файл-бриф, charter'ы несут метод.

Коммит фиксирует состояние на момент ревью: три прохода нашли
блокирующие дефекты (нет шага, заводящего бриф; git rebase на занятой
worktree ветке; sprint drop пишет наполовину) — они чинятся следующими
коммитами. Сохранено как база, от которой видно правки.
2026-08-03 11:01:29 +03:00

15 KiB

Шаблон брифа проекта

Скопируй в docs/review-brief.md и заполни. Контракт разделов — в project-brief.md; здесь только образец заполнения.

Курсивом даны пояснения — их из готового брифа убирают. Примеры взяты из двух разных проектов (коллектор данных с непрерывным потоком и связующий сервис вокруг внешних демонов), чтобы было видно, как один и тот же раздел выглядит при разной природе проекта.


Проект

Абзац: что делает — и чего не делает.

Коллектор выгрузок с телефона. Принимает доставки, хранит их и отдаёт другим сервисам. Это хранилище, а не аналитика: принять, дедуплицировать, сохранить, отдать. Не переименовывать поля источника, не интерпретировать значения; свёртка считается только в ответе на запрос.

Связующий сервис между качалкой и медиасервером: принимает задание, качает, распознаёт содержимое, раскладывает файлы ссылками. Не медиатека и не плеер — ничего не хранит сверх метаданных о раскладке.

Инварианты

Проверяемое свойство + последствие + severity по умолчанию. Цитируются формулировкой.

  • Точка сохраняется дословно. Незнакомое поле не отбрасывается, число не округляется при записи. Нарушение — необратимая потеря: сырой архив живёт 14 дней, дальше истина только в свёртке. По умолчанию critical.
  • Источник неприкосновенен. Только mkdir/link(2)/unlink собственных ссылок; файлы под каталогом загрузок не трогаются никогда. Нарушение — повреждение чужих данных, необратимое. По умолчанию critical.
  • Сохранили — значит приняли. Код ответа отражает доставку, а не разбор: непонятое содержимое — 200, тело уже на диске. Нарушение стоит доставки, которую отправитель не повторит. По умолчанию critical.
  • Секреты и данные пользователя не в логах. Тело запроса — только на DEBUG и с обрезкой. По умолчанию critical.
  • Агрегации при записи нет. Нарушение искажает историю молча и диагностируется только сверкой с внешним источником, то есть месяцами позже. По умолчанию major, critical — если испорченное невосстановимо.

Гейт

  • Команда: task gate BASE=<база>; база по умолчанию — git merge-base HEAD master, на masterHEAD~1.
  • Логи шагов: tmp/gate/<шаг>.log. Сводка печатает OK/FAIL/WARN/SKIP; краснит гейт только FAIL.
  • Шаги: сборка, vet, линтеры, форматирование, тесты, повторный прогон на флаки, -race, покрытие изменённых строк, накат миграций с нуля, поиск секретов, govulncheck.
  • Красят безусловно (перечислить с причиной — это главная часть раздела):
    • no-user-data — файл из каталога данных попал под контроль версий: убрать обычным коммитом уже нельзя;
    • config-samples — структура конфига изменилась, а образец нет: забытое поле обнаруживается не тестом, а тем, что через полгода о нём никто не знает;
    • migrations — миграции не накатываются с нуля: восстановление перестаёт работать ровно тогда, когда оно нужно;
    • er-schema — миграция тронута, а схема в документации не обновлена.
  • Чего в гейте намеренно нет: прогон на живом корпусе (task verify:archive) — минута работы и данные, которых нет ни на какой другой машине. У этой проверки краснота не видна никому до следующей задачи, которая до неё дотянется, — говори об этом в границах покрытия.

Команды

  • Карта проекта для архитектурного прохода: task review:context > tmp/review-context.md
  • Поднять изменение вживую: task restart, логи — task logs
  • Тесты и линт: task test, task lint
  • Дорогое, вручную: task verify:archive (минута, живые данные)
  • Запускать запрещено: ничего, что пишет в ./data, в рабочую БД и в боевой каталог архива. Замеры — только на копиях в ./tmp.

Прод и поток

  • Где: один статический бинарь в контейнере на домашнем сервере, перед ним обратный прокси с TLS, SQLite на диске. Ни оркестратора, ни реплик, ни дежурной смены.
  • Внешние зависимости и как каждая отказывает: прокси — рвёт соединение на длинном теле; диск — заполняется и тормозит; СУБД — отдаёт «занято» под параллельной записью; приложение-источник на телефоне — молча перестаёт слать. (В другом проекте здесь были бы качалка, медиасервер, LLM и база метаданных, и каждая — со своим «отвечает медленно», а не только «упала».)
  • Кто заметит отказ: один пользователь-владелец, в лучшем случае вечером, а скорее не заметит вовсе.
  • Характер потока: телефон шлёт непрерывно и молча; обратной связи у отправителя нет, об отказах он не сообщает, расписание плавает. Тихо сломавшаяся доставка — главный эксплуатационный риск.
  • Числа (с провенансом): нижний слой — порядка 135 тыс. точек в сутки (замер, docs/local-research.md); тела доходили до 42 МБ (там же); запись — read-modify-write под конкурентными доставками (docs/architecture.md).
  • Обратимость: падение сервиса обратимо — отправитель дошлёт широким проходом. Потеря или порча точки необратима. Поэтому тихая порча весит больше, чем «сервис вернул 500».

Модель угроз

  • Недоверенное: тело доставки целиком (имена метрик, единицы, формы точек, метки времени, глубина вложенности, размер); заголовки доставки, часть которых участвует в решениях; содержимое архива внешнего экспорта (имена файлов внутри zip мы не формировали); параметры читающего API.
  • Из чего строятся пути и ключи: файл сырого архива — raw/ГГГГ/ММ/ДД/<ulid>.json.gz, дата берётся из времени приёма, имя — из генератора идентификаторов; ключ записи — метрика + слой + начало + конец, источник в ключ не входит.
  • Разграничение: статические токены в Authorization: Bearer, раздельные на запись и на чтение; конфиг под 0600.
  • Что дороже: данные пользователя дороже токена. Путь, по которому значение доезжает до лога выше DEBUG, до ответа с ошибкой или до testdata в git, — полноценная находка, а не замечание по гигиене.
  • Вне модели: злоумышленник в локальной сети; вредоносный оператор; компрометация поставщика данных; мультиарендность. Находки этих классов не выводятся — они никогда не будут исправлены.

Карта

  • Актуальные спеки: openspec/specs/<capability>/spec.md
  • Дельта-спеки изменения: openspec/changes/<id>/specs/*/spec.md
  • Конвенции прозой: docs/conventions.md. Механизировано и потому не проверяется проходом по конвенциям: форма логов, fmt.Print*/os.Getenv/ time.Now мимо единых точек, сравнение ошибок, сторонние пакеты ошибок — всё это правила в .golangci.yml.
  • Архитектура и решения: docs/architecture.md
  • Журнал проскочивших дефектов: docs/review-journal.md
  • Единые точки: идентификаторы — internal/ident; время — store.Now(); разбор дат входного формата — один парсер в internal/parse; маппинг доменной ошибки в код ответа — одна точка в internal/httpapi; путь приёма — ingest, общий для HTTP и CLI. Инвентарь целиком выгружает task review:context.
  • Нумерованные артефакты: миграции — internal/store/migrations/NNNN_*.sql, номер монотонный, следующий свободный смотреть там же.
  • Задачи: docs/backlog/ (пайплайн только читает и сообщает исход)
  • Реальные пакеты для тестов разбора: internal/parse/testdata — там данные пользователя с вычищенными токенами, наружу не копировать
  • Временное: ./tmp (не системный /tmp)
  • Не трогать: ./data — боевой архив и БД

Типовые узлы

Род узла + 3–5 специфичных проверяемых свойств.

  • Разбор входного формата — поведение на усечённом и враждебном входе, границы размера, отсутствие паники, детерминизм, судьба незнакомых полей.
  • HTTP-обработчик приёма — валидация формы конверта до записи, лимит тела и архивная бомба, что попадает в ответ, а что в лог, отсутствие доменной логики в транспорте.
  • Обработчик читающего API — предсказуемость размера ответа, поведение при пустом диапазоне, коды ответа на невозможный запрос.
  • Репозиторий — границы транзакции, конкурентная запись того же ключа, откуда берутся время и id, что возвращается при отсутствии записи, идемпотентность повторной записи.
  • Файловое хранилище с ретеншеном — атомарность записи, поведение при неполной записи и нехватке места, что удаляется и по какому критерию, можно ли удалить лишнее.
  • CLI-команда пересборки — идемпотентность повторного прогона, поведение при отмене на середине, что остаётся после падения, отчёт для человека.
  • Клиент внешнего сервиса — таймаут, протяжка context, поведение при «медленно» против «упало», ретраи и их граница.

Триггеры

  • deep: миграция в internal/store/migrations/, новый пакет internal/*, изменение контракта читающего API, правило слияния или вывод слоя.
  • «Видимое снаружи» (то есть standard): эндпоинт, форма ответа, код ответа приёма, формат лога.
  • reimpl запускается, когда изменение вводит новое правило слияния, идентичности или разбора.

Недоступно проверке

  • Поведение внешнего приложения-источника на следующем его обновлении.
  • Что реально лежит в системе-источнике: сверить можно только ручным экспортом, а он делается раз в 2–3 месяца.
  • Поведение таблицы под объёмом нескольких лет истории и реальный профиль нагрузки.
  • Завязка внешних потребителей на текущую форму ответа.
  • Суждение «этой функциональности не должно существовать».