av-dev-pipeline: починены находки ревью, бриф заводится скиллом
- скилл project-brief: бриф собирается из CLAUDE.md, архитектуры, Taskfile и конвенций и показывается человеку. Раньше единственная инструкция по его созданию лежала внутри шаблона, поэтому деградированный режим был не аварийным, а единственным: critical по основанию «нарушен инвариант» недостижим ни на одной задаче - rebase перенесён внутрь worktree задачи: прежняя форма падала на занятой ветке, и агент уводил весь батч в провалившиеся с ложной причиной - контракт брифа дополнен восемью слотами; проверен заполнением на обоих проектах, незаполнимых нет. Прецедент healthlog вынут из общего charter'а в бриф — там он вмёрз вместе с числами - шов: пайплайн задачу не закрывает и записи учёта не трогает, урожай отдаёт списком, правило остатка — ссылкой на av-dev-tasks - деградированный абзац во всех девяти проходах, вопрос 9 в ops, пространство имён в вызовах, раздел предпосылок
This commit is contained in:
@@ -6,9 +6,10 @@ description: Проводит несколько задач разом — пл
|
||||
# Батч задач
|
||||
|
||||
Оркестратор **набора** задач. Планирует порядок, раскидывает задачи по
|
||||
изолированным worktree, каждую проводит через полный цикл `task-pipeline`, затем
|
||||
сводит в основную ветку линейной историей и делает финальную сверку. Тонкая
|
||||
обёртка над `task-pipeline` — не переизобретай её шаги, вызывай как есть.
|
||||
изолированным worktree, каждую проводит через полный цикл
|
||||
`av-dev-pipeline:task-pipeline`, затем сводит в основную ветку линейной историей
|
||||
и делает финальную сверку. Тонкая обёртка над пайплайном задачи — не
|
||||
переизобретай её шаги, вызывай как есть.
|
||||
|
||||
Работай **максимально автономно**, по тому же принципу, что и одиночный пайплайн:
|
||||
вопрос, который решать не тебе, записывается и не останавливает поток; спрашиваем
|
||||
@@ -16,8 +17,24 @@ description: Проводит несколько задач разом — пл
|
||||
рабочих данных). Механику — планирование, worktree, rebase, интеграцию, чистку —
|
||||
делаем без спроса.
|
||||
|
||||
## Предпосылки
|
||||
|
||||
- **OpenSpec и скиллы `opsx:*`** — на них стоит цикл внутри каждого сабагента и
|
||||
проход `review-specs` финальной сверки. Проекта без OpenSpec это касается так
|
||||
же, как одиночного пайплайна (см. его раздел «Предпосылки»).
|
||||
- **Скиллы зовутся с пространством имён**: `av-dev-pipeline:task-pipeline`,
|
||||
`av-dev-pipeline:review-pipeline`, `av-dev-pipeline:project-brief`. Короткое имя
|
||||
может разрешиться в устаревшую проектную копию, и это произойдёт молча — в
|
||||
charter'е сабагента пиши полное имя, он твоего контекста не видит.
|
||||
- **Проектные копии этих скиллов и агентов при установке плагина удаляются.**
|
||||
|
||||
Перед стартом прочитай `CLAUDE.md` проекта и бриф ревью (`docs/review-brief.md`):
|
||||
из него берутся команда гейта, инварианты и раскладка нумерованных артефактов.
|
||||
из него берутся **основная ветка** (раздел `## Карта` — она подставляется в
|
||||
каждую команду git ниже), команда гейта, инварианты и раскладка нумерованных
|
||||
артефактов. Брифа нет — заведи его Skill'ом
|
||||
**`av-dev-pipeline:project-brief`** один раз, до первой волны: иначе каждая
|
||||
задача батча заплатит деградированным ревью, а имя основной ветки придётся
|
||||
угадывать.
|
||||
|
||||
## Границы
|
||||
|
||||
@@ -27,6 +44,9 @@ description: Проводит несколько задач разом — пл
|
||||
- **Батч не владеет спринтом и целями.** Он сообщает исход по каждой задаче в тех
|
||||
же трёх словах, что и `task-pipeline`: сделана / не доведена / оказалась крупнее
|
||||
задачи.
|
||||
- **Записей учёта батч не трогает и задач не закрывает** — как и одиночный
|
||||
пайплайн. Закрытие — акт владельца спринта после приёмки. Урожай ревью батч
|
||||
отдаёт списком, а задачи из него заводит тот, кто ведёт задачи проекта.
|
||||
|
||||
## Ключевое отличие от одиночного пайплайна
|
||||
|
||||
@@ -62,25 +82,53 @@ fast-forward. Ветки после вливания удаляются.
|
||||
- **затронутые capability** — по её описанию и по каталогу актуальных спек
|
||||
(`openspec/specs/`);
|
||||
- **жёсткие зависимости**: задача B строится на результате A → A строго раньше B;
|
||||
- **замеряющая задача** — та, чьё ревью будет доказывать находки **числами**, и
|
||||
потому она гонится в волне **одна** (обоснование — ниже, в шаге 4). Решается
|
||||
здесь, на планировании, а не во время прогона: состав волны определяется
|
||||
сейчас, а профиль ревью сабагент выберет только внутри задачи, и ключевать
|
||||
волну на ещё не сделанный выбор нельзя. Триггеры — по фактам о задаче, каждый
|
||||
сам по себе достаточен:
|
||||
- трогает схему хранилища, миграцию, формат на диске или объём хранимого;
|
||||
- трогает конкурентность: транзакции, блокировки, фоновые циклы, общее
|
||||
состояние;
|
||||
- трогает размер тела, буфер, память, сжатие, ретеншен, темп потока;
|
||||
- её тема названа в разделах `## Прод и поток` или `## Прецеденты` брифа как
|
||||
место, где уже мерили или уже ломалось.
|
||||
|
||||
Ни один триггер не сработал — задача не замеряющая, даже если её ревью
|
||||
окажется `deep`. `deep` про глубину проверки, замеряющая — про соревнование за
|
||||
железо; это разные вопросы, и совпадают они не всегда;
|
||||
- **нумерованные артефакты — номера раздаёт оркестратор заранее.** Если проект
|
||||
нумерует миграции или подобные файлы (путь — из брифа), посмотри последний
|
||||
номер и **раздай номера тем задачам, которые, вероятно, их добавят**, до
|
||||
номер и **раздай номера всем задачам, которые, вероятно, их добавят**, до
|
||||
запуска. Номер уходит в charter сабагента, и он берёт назначенный, а не
|
||||
«следующий свободный». Так такие задачи можно гнать одновременно: файлы не
|
||||
столкнутся, а описание схемы правят разные строки — конфликт мелкий и решается
|
||||
на интеграции;
|
||||
«следующий свободный».
|
||||
|
||||
**Это отдельная механика от правила волны, и она ему не служит** — их раньше
|
||||
путали, и они тянули в разные стороны. Правило волны отвечает на вопрос «кто с
|
||||
кем гонится одновременно», предраздача — на вопрос «какой номер берёт задача».
|
||||
Раздача нужна там, где **две задачи одной под-пачки** добавляют нумерованный
|
||||
артефакт: каждая считает «следующий свободный» по основной ветке, которая ещё
|
||||
не видела соседку, и обе берут один номер. Миграции под это почти не попадают —
|
||||
миграция и так триггер замеряющей задачи, а замеряющая идёт одна; но
|
||||
нумерованные артефакты бывают не только миграциями. Поэтому номера раздаются
|
||||
**всем** задачам с таким артефактом, независимо от того, в какой волне они
|
||||
окажутся: раздача ничего не стоит, а её отсутствие ловится только конфликтом на
|
||||
интеграции. Между волнами проблемы нет — ветка следующей волны берётся от
|
||||
вершины, уже включающей предыдущие;
|
||||
- **жёстко сериализуем** (не гоняем одновременно) настоящие пересечения:
|
||||
- **одна capability на несколько задач** — две задачи, правящие одну спеку (тем
|
||||
более одно и то же `### Requirement`), дают не текстовый, а **семантический**
|
||||
конфликт при архивации; сериализуем по смыслу, а не только по файлам;
|
||||
- пересечение по одним и тем же исходникам;
|
||||
- **мягкие конфликты** сериализовать не надо: индекс беклога (каждая задача
|
||||
убирает свою строку) и спеки разных capability — разные строки и файлы,
|
||||
сливаются сами.
|
||||
- **мягкие конфликты** сериализовать не надо: файлы-перечни, где каждая задача
|
||||
правит **свою** строку (индексы, оглавления, списки записей), и спеки разных
|
||||
capability — разные строки и файлы, сливаются сами.
|
||||
|
||||
Собери план: **волны** параллельно-безопасных задач плюс сериализованный хвост
|
||||
конфликтоопасных, с учётом зависимостей. Покажи план короткой репликой и иди
|
||||
дальше.
|
||||
конфликтоопасных, с учётом зависимостей; замеряющие задачи стоят в плане
|
||||
отдельными волнами по одной. Покажи план короткой репликой — назвав, какие
|
||||
задачи признаны замеряющими и по какому триггеру, — и иди дальше.
|
||||
|
||||
### 3. Свежая база
|
||||
|
||||
@@ -91,16 +139,17 @@ fast-forward. Ветки после вливания удаляются.
|
||||
### 4. Прогнать волны
|
||||
|
||||
**Потолок параллелизма — 2–3 задачи одновременно.** Каждая задача тянет полный
|
||||
`task-pipeline` с вложенным ревью и гейтом, поэтому больше трёх разом душат
|
||||
машину и провоцируют гонки. Волну шире трёх бей на под-пачки по ≤3.
|
||||
цикл пайплайна с вложенным ревью и гейтом, поэтому больше трёх разом душат
|
||||
машину и провоцируют гонки. Волну шире трёх бей на под-пачки по ≤3 и **гони
|
||||
под-пачки последовательно**: следующая стартует, когда предыдущая вернула отчёты.
|
||||
Иначе потолок обходится тривиально — шесть задач, запущенных «двумя под-пачками»
|
||||
в одном сообщении, это шесть задач разом.
|
||||
|
||||
**Волна из одной задачи — не вырожденный случай, а обязательный.** Задача,
|
||||
ревью которой будет доказывать находки **числами** (профиль `deep`, где работают
|
||||
`adversary` и `ops`: удержание блокировки, пик памяти, рост файлов, длительность
|
||||
операции), гонится в волне одна. Соседний прогон на той же машине портит эти
|
||||
числа, а находка с испорченным оракулом хуже отсутствующей — она выглядит
|
||||
доказанной. Если задача всё же пошла в общей волне, её отчёт обязан нести строку
|
||||
в границах покрытия: замеры сняты под соседней нагрузкой.
|
||||
признанная на шаге 2 **замеряющей**, гонится в волне одна: соседний прогон на той
|
||||
же машине портит числа, а находка с испорченным оракулом хуже отсутствующей — она
|
||||
выглядит доказанной. Если замеряющая задача всё же пошла в общей волне, её отчёт
|
||||
обязан нести строку в границах покрытия: замеры сняты под соседней нагрузкой.
|
||||
|
||||
Для каждой задачи в под-пачке:
|
||||
|
||||
@@ -111,19 +160,28 @@ fast-forward. Ветки после вливания удаляются.
|
||||
`subagent_type: general-purpose`. Charter сабагента:
|
||||
- работай **строго в своём worktree** `<path>`; в другие каталоги и в основную
|
||||
ветку не лезь;
|
||||
- прогони Skill **`task-pipeline`** ровно на этой задаче, полный цикл SDD с
|
||||
обоими чекпоинтами ревью;
|
||||
- прогони Skill **`av-dev-pipeline:task-pipeline`** ровно на этой задаче,
|
||||
полный цикл SDD с обоими чекпоинтами ревью;
|
||||
- если задаче назначен **номер артефакта** — используй строго его;
|
||||
- **профиль ревью выбирается по факту изменения.** Батч не повод понижать
|
||||
профиль: «нас много и мы спешим» — это ровно тот стимул, из-за которого
|
||||
проходы пропускают;
|
||||
- **режим прогона проходов — последовательный.** Твой worktree не один на
|
||||
машине;
|
||||
- **если вложенные сабагенты недоступны** (движок не даёт запускать агентов из
|
||||
агента) — не пропускай ревью и не понижай профиль: проведи его **инлайн** по
|
||||
тем же charter'ам `av-dev-pipeline`, сохранив обязательное — гейт до
|
||||
опиниативных проходов, состав по профилю, триаж последним. И **скажи в
|
||||
отчёте прямым текстом, что ревью шло инлайн**: инлайновый проход видит
|
||||
контекст автора и потому декоррелирован слабее — это меняет доверие к
|
||||
результату, а не только способ запуска;
|
||||
- **вернуть отчёт**, в котором обязательно: исход задачи одним из трёх слов;
|
||||
что сделано; какие вопросы записаны и куда; изменённые файлы; добавлялся ли
|
||||
нумерованный артефакт и с каким номером; затронутые capability; состояние
|
||||
гейта; **перечень запущенных проходов ревью поимённо с исходом каждого** и
|
||||
границы покрытия.
|
||||
**объявленный профиль ревью и режим прогона**; что сделано; какие вопросы
|
||||
записаны и куда; изменённые файлы; добавлялся ли нумерованный артефакт и с
|
||||
каким номером; затронутые capability; состояние гейта; **перечень
|
||||
запущенных проходов ревью поимённо с исходом каждого**; **путь к
|
||||
сохранённому отчёту триажа** (`openspec/changes/<id>/review/`); шло ли ревью
|
||||
инлайн; границы покрытия.
|
||||
|
||||
Сабагент, упершийся в вопрос, **не останавливает батч**: он записывает вопрос,
|
||||
режет задачу до остатка и доводит остаток — либо, если остатка нет, возвращает
|
||||
@@ -132,40 +190,86 @@ fast-forward. Ветки после вливания удаляются.
|
||||
|
||||
### 5. Проверить полноту ревью — до интеграции
|
||||
|
||||
**Ветка, чей отчёт не называет проходы поимённо, не вливается.** Пропуск прохода
|
||||
не отличим от прохода без находок, и на уровне батча это ещё опаснее: отчётов
|
||||
много, каждый выглядит полным, а сверять их некому, кроме тебя.
|
||||
**Ветка, чей отчёт не называет профиль и проходы поимённо, не вливается.**
|
||||
Пропуск прохода не отличим от прохода без находок, и на уровне батча это ещё
|
||||
опаснее: отчётов много, каждый выглядит полным, а сверять их некому, кроме тебя.
|
||||
|
||||
По каждой готовой ветке сверь перечень проходов с таблицей профилей скилла
|
||||
`review-pipeline` для объявленного профиля. Расхождение — не повод отменять
|
||||
задачу: дозапусти недостающие проходы **на ветке**, в её worktree, через
|
||||
`review-pipeline`, и только потом интегрируй. Отчёт дозапуска приложи к отчёту
|
||||
задачи.
|
||||
Сверка идёт в три шага, и порядок важен:
|
||||
|
||||
1. **Возьми объявленный профиль** из отчёта задачи — он затем и заказан в
|
||||
обязательных полях шага 4. Профиля в отчёте нет — перечень проходов сверять
|
||||
не с чем; это само по себе основание не вливать, пока сабагент не назовёт
|
||||
профиль и не обоснует его по факту изменения.
|
||||
2. **Сверяй с независимым артефактом, а не с прозой отчёта.** Перечень проходов
|
||||
бери из **сохранённого отчёта триажа** (`openspec/changes/<id>/review/`) —
|
||||
пайплайн обязан его туда положить. Проза сабагента написана тем же, кто мог
|
||||
проход и пропустить: она подтверждает сама себя. Отчёта триажа на месте нет —
|
||||
считай, что состав неизвестен, и дозапускай ревью целиком.
|
||||
3. **Сверь состав** с таблицей профилей скилла
|
||||
`av-dev-pipeline:review-pipeline` для объявленного профиля.
|
||||
|
||||
Расхождение — не повод отменять задачу: дозапусти недостающие проходы **на
|
||||
ветке**, в её worktree, через `av-dev-pipeline:review-pipeline`, и только потом
|
||||
интегрируй.
|
||||
|
||||
**Находки дозапуска — такие же находки, и зелёный гейт их не отменяет.** Правило
|
||||
интеграции «вливаем только зелёные» смотрит на гейт, а дозапущенный `critical`
|
||||
гейт не красит: он был бы пропущен молча, если это не сказать прямо. Поэтому:
|
||||
|
||||
- `critical` или `major` из дозапуска — **вливание этой ветки останавливается**.
|
||||
Помеченное `инлайн` чинится в её worktree, после починки — гейт, затем
|
||||
интеграция. Помеченное `развилка` — вопрос в запись, задача режется до остатка
|
||||
ровно так же, как это сделал бы пайплайн внутри;
|
||||
- остатка нет — ветка не вливается и уходит в доклад как провалившаяся, со своим
|
||||
worktree;
|
||||
- `minor` и `nit` из дозапуска — в урожай доклада, вливанию не мешают.
|
||||
|
||||
Отчёт дозапуска приложи к отчёту задачи и назови в докладе (шаг 9), почему он
|
||||
понадобился: систематический пропуск одного и того же прохода — находка о самом
|
||||
конвейере, а не о задаче.
|
||||
|
||||
### 6. Интегрировать — rebase + fast-forward, по одной ветке
|
||||
|
||||
Сводим ветки **строго последовательно** (линейная история), в порядке
|
||||
зависимостей. Вливаем **только зелёные**.
|
||||
|
||||
**Ветка задачи занята её worktree, и это определяет форму команд.** Пока worktree
|
||||
жив (а удаляется он последним, после зелёного гейта), ветка `task/<slug>`
|
||||
checkout'нута в нём, и `git rebase <основная> task/<slug>` из главного worktree
|
||||
**падает**: `fatal: 'task/<slug>' is already used by worktree at …`. Поэтому
|
||||
rebase делается **внутри worktree задачи**, а ff-слияние — из главного.
|
||||
|
||||
Для каждой готовой ветки `task/<slug>`:
|
||||
|
||||
- `git rebase <основная> task/<slug>` — перенос на текущую вершину;
|
||||
- `git -C <path> rebase <основная>` — перенос ветки задачи на текущую вершину,
|
||||
выполняется в её собственном worktree;
|
||||
- резолв конфликтов (их почти нет — конфликтоопасное сериализовано, номера
|
||||
розданы заранее). Неавтоматический конфликт — **не форсируй**: прерви
|
||||
(`git rebase --abort`), оставь ветку и worktree как есть, вынеси это в доклад
|
||||
как нераспознанное пересечение;
|
||||
- `git checkout <основная> && git merge --ff-only task/<slug>`;
|
||||
(`git -C <path> rebase --abort`), оставь ветку и worktree как есть, вынеси это
|
||||
в доклад как нераспознанное пересечение;
|
||||
- **ненулевой код `rebase` относится к этой ветке и только к ней.** Прерванный
|
||||
rebase в чужом worktree не трогает ни главное дерево, ни остальные ветки:
|
||||
проверь `git -C <path> status` и `git status` — обе чистые. Уводить весь батч
|
||||
в провалившиеся из-за одного ненулевого кода запрещено: это ложная причина,
|
||||
из-за которой зелёные задачи не доедут до основной ветки. Провалилась одна —
|
||||
провалилась одна;
|
||||
- из главного worktree (он стоит на основной ветке — проверь
|
||||
`git rev-parse --abbrev-ref HEAD`): `git merge --ff-only task/<slug>`. Ветку в
|
||||
главном дереве **не переключай** — `git checkout task/<slug>` тоже упрётся в
|
||||
занятость;
|
||||
- после каждой интеграции — **гейт на основной ветке**. Красное — **откати эту
|
||||
интеграцию** (`git reset --hard` на прошлую вершину), ветку с worktree сохрани,
|
||||
задачу перечисли в докладе. Основная ветка **никогда** не остаётся
|
||||
полузелёной;
|
||||
- только после зелёного: `git worktree remove <path>` и
|
||||
`git branch -d task/<slug>`.
|
||||
`git branch -d task/<slug>` — в этом порядке, иначе ветка снова занята.
|
||||
|
||||
**Политика частичного провала.** Упавшая задача (исход «не доведена», красные
|
||||
тесты в её worktree, конфликт при rebase) **не блокирует остальные**: интегрируем
|
||||
все зелёные, упавшую оставляем в её worktree и ветке нетронутой — ничего не
|
||||
удаляем, — и перечисляем в докладе с причиной, отчётом и путём к worktree.
|
||||
тесты в её worktree, конфликт при rebase, невлитая из-за находок дозапуска)
|
||||
**не блокирует остальные**: интегрируем все зелёные, упавшую оставляем в её
|
||||
worktree и ветке нетронутой — ничего не удаляем, — и перечисляем в докладе с
|
||||
причиной, отчётом и путём к worktree. Причина называется **настоящая**: «конфликт
|
||||
rebase в файле X», а не «нераспознанное пересечение» на всякий случай.
|
||||
|
||||
### 7. Финальный гейт
|
||||
|
||||
@@ -191,7 +295,7 @@ fast-forward. Ветки после вливания удаляются.
|
||||
уже делается» — именно он возникает, когда две задачи независимо решали
|
||||
похожее.
|
||||
|
||||
Замечания отрабатывай как в `task-pipeline`: `инлайн` чини сам, `развилка` —
|
||||
Замечания отрабатывай как одиночный пайплайн: `инлайн` чини сам, `развилка` —
|
||||
вопросом в запись; после правок — снова гейт.
|
||||
|
||||
### 9. Прибраться и доложить
|
||||
@@ -199,16 +303,22 @@ fast-forward. Ветки после вливания удаляются.
|
||||
- Убери worktree и ветки **только успешно влитых** задач, в конце
|
||||
`git worktree prune`. Worktree и ветки **провалившихся** не трогай — они нужны
|
||||
для ручного дожатия.
|
||||
- **Задачи батч не закрывает** — ни одну, ни свои, ни чужие записи учёта не
|
||||
трогает. Он сообщает исход по каждой; закрытие происходит после приёмки и
|
||||
делается владельцем спринта.
|
||||
- Доложи кратко:
|
||||
- **исход по каждой задаче** одним из трёх слов, с хешем коммита;
|
||||
- план волн и порядок интеграции;
|
||||
- план волн и порядок интеграции, с пометкой, какие задачи шли по одной как
|
||||
замеряющие;
|
||||
- вопросы, записанные сабагентами, пачкой;
|
||||
- что дозапускалось на шаге 5 и почему;
|
||||
- что дозапускалось на шаге 5 и почему; шло ли где-то ревью инлайн;
|
||||
- итог финальной сверки и ссылки на архивные change;
|
||||
- **отдельно — провалившиеся** задачи с причиной и путём к оставленному
|
||||
worktree;
|
||||
- **`Урожай`** — отложенные находки всех задач одним списком, с провенансом.
|
||||
Задачи из него заводит тот, кто ведёт задачи проекта, а не батч;
|
||||
- **отдельно — провалившиеся** задачи с настоящей причиной и путём к
|
||||
оставленному worktree;
|
||||
- **границы покрытия сводной строкой**, включая задачи, чьи замеры снимались в
|
||||
общей волне.
|
||||
общей волне, и ветки, где ревью шло инлайн.
|
||||
|
||||
## Тонкости
|
||||
|
||||
@@ -219,9 +329,9 @@ fast-forward. Ветки после вливания удаляются.
|
||||
- Поведенческая верификация внутри сабагента поднимает изменение вживую: следи,
|
||||
чтобы соседние worktree не дрались за порты и рабочие каталоги. Если проект
|
||||
умеет поднимать только один экземпляр — такие задачи в одну волну не ставь.
|
||||
- Ревью выполненного — **до** закрытия задачи; это забота `task-pipeline` внутри
|
||||
каждого сабагента, дублировать не надо.
|
||||
- `openspec validate --strict` тоже внутри `task-pipeline` — не пропускай его
|
||||
- Ревью выполненного — **до** интеграции; это забота
|
||||
`av-dev-pipeline:task-pipeline` внутри каждого сабагента, дублировать не надо.
|
||||
- `openspec validate --strict` тоже внутри пайплайна задачи — не пропускай его
|
||||
своими правками на интеграции.
|
||||
- Крупная переработка, предложенная ревью внутри задачи, — развилка: не вливай
|
||||
молча, вынеси в доклад.
|
||||
|
||||
Reference in New Issue
Block a user