Pipeline: проект от идеи до выката
Сегодня: !date +%F. Все даты в артефактах берёшь отсюда, а не по памяти —
своей текущей даты ты не знаешь.
Одиннадцать стадий с жёсткими воротами между ними. Ты ведёшь пользователя по стадиям,
материализуя каждую в файл. Файлы — единственный источник правды: контекст сессии
умирает, docs/pipeline/ живёт.
0. context load — project brief подгружается, не пересказывается
1. idea — что и зачем, в 3–5 предложениях
2. plan + criteria — шаги + явное «готово = ...»
3. risk spikes — только реально неизвестное, код на выброс
4. contracts — модели, сигнатуры, схемы ответов, формат ошибок
5. vertical slice — один сквозной путь end-to-end
6. tasks — атомарные, у каждой свой критерий готовности
7. implementation — по одной задаче за сессию
8. audit — в свежем контексте: тесты + ручные проверки
9. feedback — находки аудита → обратно в brief + decision log
10. release — выкат: пред-запусковые проверки, доступы, бэкапы, откат
Стадии 0–9 — цикл разработки, он повторяется. Стадия 10 проходится не каждый круг, а перед реальной публикацией.
Артефакты
Всё в docs/pipeline/ в корне проекта:
| Файл | Стадия | Природа |
|---|---|---|
state.md |
все | текущая стадия + статус ворот. Обновляй каждый раз |
brief.md |
1, 5, 9 | живой документ: что строим, зачем, ограничения, стек, как запускать |
plan.md |
2 | шаги + acceptance criteria (проверяемые) |
spikes/ |
3 | код на выброс + spikes/FINDINGS.md с ответами |
contracts.md |
4 | модели данных, сигнатуры, схемы, конфиг, ошибки |
tasks.md |
6 | атомарные задачи с DoD-чекбоксами |
audit.md |
8 | отчёт аудита свежим контекстом |
decisions.md |
9 (append) | лог решений: дата, решение, альтернативы, почему |
release.md |
10 | что выкачено, куда, чем проверено, как откатить |
Шаблоны — в references/templates.md. Детали по стадиям — в references/stages.md.
Читай нужный раздел stages.md перед началом стадии, а не по памяти.
Правило ворот (нарушать нельзя)
В конце каждой стадии:
- Запиши артефакт на диск.
- Обнови
state.md. - Покажи сжатую сводку: суть + что решили. Не пересказ файла.
- Задай явный вопрос про переход и остановись. Следующую стадию не начинаешь, пока пользователь не сказал «да / дальше / поехали».
Исключение: пользователь явно сказал «не спрашивай, гони до конца». Тогда идёшь подряд, но артефакты всё равно пишешь и в конце показываешь их список.
Правило нулевой стадии
brief.md подгружается, не пересказывается. Прочитал — работай с этим знанием молча.
Пересказ brief пользователю запрещён: он его писал. Максимум — одна строка
«brief загружен: <проект>, стадия N, следующая задача T-xx».
Старт сессии
- Есть
docs/pipeline/state.md? → стадия 0: прочитайbrief.md,state.md, и артефакт текущей стадии. Одна строка подтверждения → продолжай с зафиксированной стадии. - Нет? → новый проект: создай
docs/pipeline/, разведай окружение (стадия 0), переходи к стадии 1. - Пользователь назвал стадию явно («давай контракты») — прыгай туда, но сначала прочитай артефакты предыдущих стадий. Не выдумывай контекст, которого не читал.
Проект, где код уже есть (brownfield)
Пайплайн написан под чистый лист. В существующем проекте стадии те же, но три из них работают наоборот — не проектируешь, а извлекаешь:
- 1 idea. Brief пишется по факту: что система делает сейчас, и отдельным разделом — что мы хотим изменить. Не переписывай замысел автора, ты его не знаешь.
- 4 contracts. Контракты вычитываются из кода: реальные сигнатуры, реальные модели,
реальный формат конфига. Расхождение «как написано» и «как задумано» — находка,
её в
decisions.md, а не молчаливое «исправление». - 5 vertical slice. Слайс уже существует. Вместо написания — запусти имеющийся сквозной путь и покажи вывод. Не запускается — это первая задача, а не повод идти дальше.
Скоуп при этом сужается до изменения: пайплайн ведём по той части системы, которую трогаем, а не по всему репозиторию. Полная инвентаризация чужого кода — отдельная работа, и её не делают «заодно».
Стадии (кратко; развёрнуто — в references/stages.md)
0. Context load. Повторная сессия: подгрузить brief + state молча. Новый проект: разведка инструментами — что в репо, версии рантаймов, git/CI/тесты, ОС-специфика. Не спрашивай пользователя о том, что можешь проверить сам.
1. Idea → brief.md. Вытащи суть вопросами пачкой (AskUserQuestion, не по одному).
Кто пользователь, какую боль решаем, что считается успехом, что явно вне скоупа,
жёсткие ограничения. Итог — 3–5 предложений, подтверждённых пользователем, плюс
изученные внешние зависимости (доки, лицензии, ToS). Кладётся в brief.md.
2. Plan + criteria → plan.md. 3–7 крупных шагов. К каждому — «готово = ...» в форме «дано → когда → тогда». Критерий проверяй вопросом «как я проверю это, не спрашивая мнения?». Нет ответа — переписывай. Отдельно — non-goals.
3. Risk spikes → spikes/. Только то, чего мы не знаем и что может обрушить план.
Один спайк — один вопрос да/нет + факты. Код в spikes/ на выброс: не рефачить,
прод-код его не импортирует. Ответы — в spikes/FINDINGS.md. Спайк убил допущение —
возврат на стадию 2, это успех спайка, а не провал.
4. Contracts → contracts.md. Модели данных, публичные сигнатуры, схемы хранения
и ответов, формат конфига, таксономия ошибок. Пиши как код, не прозой. Реализации нет —
только формы. Проверка: по контрактам можно написать заглушки, и они состыкуются типами.
Здесь же — секреты и окружение: какие ключи нужны, откуда берутся, что лежит
в .env.example, что закрыто .gitignore. Ни один секрет в контракт не вписывается
значением, только именем переменной.
5. Vertical slice. Один сквозной путь: вход → обработка → выход, реально работающий.
Самый узкий из возможных, всё постороннее захардкожено. Реальные зависимости, не моки.
Запусти и покажи вывод. Стадия не закрыта, пока слайс не отработал вживую.
Сработало — сразу впиши в brief.md раздел «Как запускать» теми командами, которые
только что отработали. Дальше по ним работают и будущие сессии, и аудитор на стадии 8.
6. Tasks → tasks.md. Атомарные: одна задача по объёму = один осмысленный коммит. У каждой — файлы, что делаем, DoD, зависимости. Порядок в файле = порядок выполнения.
7. Implementation. По одной задаче за сессию. Сделал → проверил DoD фактически
(запуск/тест, не «выглядит правильно») → отметил [x] → отчёт в 1–3 строки → остановился
и спросил. Задача разрослась — стоп, дроби в tasks.md, потом делай.
Правило трёх попыток. DoD не сходится после трёх заходов — прекрати чинить. Это не задача не решается, это задача поставлена неверно: не хватает знания (→ спайк, стадия 3), неверен контракт (→ стадия 4) или DoD непроверяем (→ стадия 6). Доложи пользователю, что перепробовал и какой из трёх случаев видишь. Четвёртая попытка тем же способом — самый дорогой способ потратить сессию.
8. Audit → audit.md. Субагент (Agent, general-purpose) со свежим контекстом.
Он не видел, как писался код, — в этом смысл. Проверяет acceptance criteria запуском:
тесты + ручные проверки. Промпт — по шаблону из references/stages.md. Сам аудит не проводишь.
9. Feedback. Находки аудита → обратно в brief.md (привести к тому, что построено)
и в decisions.md (append-only, абсолютные даты). Оставшиеся дефекты — задачами в tasks.md.
Следующий проход начинается со стадии 7, а не с 1.
10. Release → release.md. Только если проект реально куда-то выкатывается. Аудит
проверял, что код делает обещанное; выкат проверяет, что он переживёт встречу
с внешним миром: секреты, доступы, лимиты, бэкапы, откат.
Прогоняется отдельными скиллами, а не по памяти: /launch-security для любого
приложения с сервером или чужими данными, /launch-web для публичного сайта.
Их отчёты ложатся в docs/launch/, находки P1 идут задачами в tasks.md и чинятся
до выката, остальное — в бэклог. В release.md фиксируешь: что выкачено, куда, какой
версией/коммитом, чем проверено после выката, как откатить и у кого доступы.
Скиллы /launch-* вызывает пользователь — сам ты их не запускаешь, скажи одной
строкой, что пора.
Роутинг: кто выполняет стадию
Модель главного цикла переключить изнутри нельзя — её задаёт пользователь через /model.
Поэтому роутинг устроен так: мышление остаётся в главном цикле, рутина уходит
субагентам, у которых модель прибита в их определении. Имена моделей живут там,
а не здесь: скилл говорит про роли и переживает смену поколений моделей.
| Стадия | Кто выполняет | Почему |
|---|---|---|
| 0 context load | главный цикл | дёшево, пара команд |
| 1 idea | главный цикл | диалог с пользователем, делегировать нечего |
| 2 plan + criteria | главный цикл, сильная модель | здесь решается судьба проекта |
| 3 risk spikes | субагент pipeline-spike |
замкнутая механика: скрипт → запуск → факты |
| 4 contracts | главный цикл, сильная модель | архитектура; ошибка тут стоит дороже всего |
| 5 vertical slice | главный цикл | проверяем контракты собой, не чужими руками |
| 6 tasks | главный цикл | разбиение требует всей картины |
| 7 implementation | по типу задачи, см. ниже | |
| 8 audit | субагент pipeline-auditor |
нужен свежий контекст, а не дешёвая модель |
| 9 feedback | главный цикл | |
| 10 release | главный цикл + скиллы /launch-* |
проверки вызывает пользователь |
Стадия 7 — исполнитель задачи решается на стадии 6, не на бегу. У каждой задачи
в tasks.md проставляй поле Исполнитель::
субагент— рутина: boilerplate, тесты по готовому контракту, CLI-обвязка, парсинг по описанной схеме, механические правки. Отдаёшь субагентуpipeline-task.главный цикл— задача трогает архитектуру, требует решений или диалога с пользователем. Делаешь сам. Если сомневаешься — главный цикл: делегирование не бесплатно, субагент заново поднимает контекст, и на неоднозначной задаче это дороже, чем сделать самому.
Что сказать пользователю про его сторону: на стадиях 2 и 4 держать самую сильную доступную
модель, на 7 при пачке рутины — можно переключиться на быструю. Актуальные имена он видит
в /model, там же есть комбинированные режимы «планирование сильной, исполнение быстрой».
Говори это один раз на воротах нужной стадии, не напоминай каждое сообщение.
Стиль кода — действует на всех стадиях
Комментарии
Комментарий объясняет почему, а не что. Что делает код — видно из кода.
Пиши комментарий, только если он несёт знание, которого в коде нет:
- неочевидное внешнее ограничение («у OLX
private_businessвозвращает 200 и молча не фильтрует — рабочий параметрowner_type») - причина обходного пути и что сломается без него
- откуда взялось магическое значение (ссылка на спайк, тикет, замер)
- намеренный отказ от очевидного решения и его причина
Не пиши никогда:
# Увеличиваем счётчик на единицу
counter += 1
# Функция для получения объявлений
def get_listings(...):
# ---------- ХЕЛПЕРЫ ----------
Это шум: он повторяет код, устаревает первым и приучает не читать сам код.
Докстринги — только там, где неочевидны контракт, единицы измерения или побочные эффекты.
Тривиальной функции докстринг не нужен. Докстринг, пересказывающий имя функции
("""Сколько спать до следующего опроса""" над seconds_until_next), — чистый шум.
Измеримые ориентиры (словесного «подгоняй под окружающий код» на практике не хватает):
- Комментариев в файле — примерно 5–8% от строк кода. Выше 12% — почти наверняка объяснение самого себя, а не кода.
- Над одиночным вызовом — не больше одной строки комментария. Три строки обоснования над одной строкой кода — это сообщение коммита, а не комментарий.
- Разброс плотности между файлами одного проекта — признак поломки. Если в одном модуле 25%, а в другом 3%, значит, комментировался ход мыслей: гуще там, где дольше думал.
Проверяй себя вопросом: «это знание или мой мыслительный процесс?» Про чужой API, про грабли, про причину неочевидного выбора — знание, оставляй. Про то, как ты пришёл к решению, — процесс, выкидывай.
Не объясняй отсутствие. «Справочники сюда не входят», «здесь намеренно нет ретрая» — бесконечный жанр: к любой строке можно дописать, чего в ней нет. Комментируй то, что есть. Исключение — если отсутствие выглядит как забытое и кто-то полезет «чинить».
Не пересказывай общеизвестное. «Зависимости отдельным слоем, чтобы не пересобирались» в Dockerfile, «индекс ускоряет выборку» у CREATE INDEX — это знает всякий, кто читает такой файл. Правило действует и вне кода: YAML, Dockerfile, конфиги — там та же норма.
В файле без комментариев не начинай их расставлять.
Git
- Не указывай себя в авторах или контрибьюторах. Никаких
Co-Authored-By,Generated with, упоминаний модели в теле коммита или PR. Автор — пользователь. - Сообщение коммита — о сути изменения, в стиле, уже принятом в репозитории
(посмотри
git log, прежде чем писать первый коммит). - Коммитишь и пушишь только по явной просьбе. «Одна задача = один коммит» из стадии 6 — это мера размера задачи, а не разрешение коммитить самому: задача должна быть такой, чтобы её изменения складывались в один осмысленный коммит.
- Закрыв задачу на стадии 7, в отчёте одной строкой предложи готовое сообщение коммита. Пользователь коммитит сам или говорит «коммить». Молчание — не согласие.
- Перед первым коммитом проверь, что в индекс не попали
.env, ключи, дампы и мусор сборки. Секрет, попавший в историю, чистится ротацией ключа, а неgit rm.
Как себя вести внутри стадии
- Не забегай вперёд. На контрактах не пиши реализацию, на плане не пиши контракты. Самая частая поломка пайплайна — «я тут заодно уже накидал».
- Не выдумывай факты. Не знаешь, как ведёт себя внешний сервис или библиотека — это спайк (стадия 3) или веб-поиск, а не догадка в brief.
- Решения фиксируй сразу черновой строкой, на стадии 9 соберёшь в
decisions.md. - Артефакты — на языке пользователя, код и идентификаторы — по-английски.