Files
dev-skills/av-dev-pipeline/skills/task-batch/SKILL.md
T
av 0eca206460 av-dev-pipeline: починены находки ревью, бриф заводится скиллом
- скилл project-brief: бриф собирается из CLAUDE.md, архитектуры, Taskfile
  и конвенций и показывается человеку. Раньше единственная инструкция по
  его созданию лежала внутри шаблона, поэтому деградированный режим был не
  аварийным, а единственным: critical по основанию «нарушен инвариант»
  недостижим ни на одной задаче
- rebase перенесён внутрь worktree задачи: прежняя форма падала на занятой
  ветке, и агент уводил весь батч в провалившиеся с ложной причиной
- контракт брифа дополнен восемью слотами; проверен заполнением на обоих
  проектах, незаполнимых нет. Прецедент healthlog вынут из общего charter'а
  в бриф — там он вмёрз вместе с числами
- шов: пайплайн задачу не закрывает и записи учёта не трогает, урожай
  отдаёт списком, правило остатка — ссылкой на av-dev-tasks
- деградированный абзац во всех девяти проходах, вопрос 9 в ops,
  пространство имён в вызовах, раздел предпосылок
2026-08-03 11:45:40 +03:00

31 KiB

name, description
name description
task-batch Проводит несколько задач разом — планирует порядок и пересечения, гонит каждую задачу отдельным сабагентом в своём git worktree через task-pipeline, интегрирует по одной ветке через rebase + fast-forward (линейная история), проверяет полноту ревью каждой ветки и в конце сверяет стыки, возникшие от слияния. Набор задач приходит извне. Использовать, когда просят сделать несколько задач сразу.

Батч задач

Оркестратор набора задач. Планирует порядок, раскидывает задачи по изолированным worktree, каждую проводит через полный цикл av-dev-pipeline:task-pipeline, затем сводит в основную ветку линейной историей и делает финальную сверку. Тонкая обёртка над пайплайном задачи — не переизобретай её шаги, вызывай как есть.

Работай максимально автономно, по тому же принципу, что и одиночный пайплайн: вопрос, который решать не тебе, записывается и не останавливает поток; спрашиваем только про необратимое (деплой, выкладка наружу, удаление или перезапись рабочих данных). Механику — планирование, 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 один раз, до первой волны: иначе каждая задача батча заплатит деградированным ревью, а имя основной ветки придётся угадывать.

Границы

  • Набор задач приходит извне. Батч его не формирует: не выбирает из беклога, не приоритизирует, не решает, что важнее. Набор не задан — попроси его у вызывающего и остановись.
  • Батч не владеет спринтом и целями. Он сообщает исход по каждой задаче в тех же трёх словах, что и task-pipeline: сделана / не доведена / оказалась крупнее задачи.
  • Записей учёта батч не трогает и задач не закрывает — как и одиночный пайплайн. Закрытие — акт владельца спринта после приёмки. Урожай ревью батч отдаёт списком, а задачи из него заводит тот, кто ведёт задачи проекта.

Ключевое отличие от одиночного пайплайна

task-pipeline коммитит в текущую ветку, и при ручном запуске это основная ветка. Здесь так нельзя для параллельных задач, поэтому батч — осознанное исключение: временные ветки и worktree заводятся лишь как средство изоляции, а конечное состояние — та же линейная история основной ветки через rebase + fast-forward. Ветки после вливания удаляются.

Модель исполнения

  • Каждая задача = один автономный сабагент (general-purpose, чтобы иметь доступ к Skill и Agent для вложенных чекпоинтов ревью), работающий только в своём worktree и прогоняющий task-pipeline целиком на этой задаче.
  • Оркестратор кода задач не пишет: он планирует, заводит worktree, запускает сабагентов, проверяет полноту их ревью, интегрирует ветки и делает финальную сверку.
  • Стиль правок внутри — заточка под проект и конвенции, right-size, без золочения.

Шаги

1. Прочитать набор

Набор задан списком (слаги, файлы, описания) — прочитай файл каждой задачи и связанные спеки и черновики. Задачи-идеи включаются, но помни: сабагент проведёт их сперва через opsx:explore, это тяжелее и чаще упирается в вопрос.

2. Спланировать порядок и пересечения (автономно)

Для каждой задачи определи:

  • затронутые capability — по её описанию и по каталогу актуальных спек (openspec/specs/);

  • жёсткие зависимости: задача B строится на результате A → A строго раньше B;

  • замеряющая задача — та, чьё ревью будет доказывать находки числами, и потому она гонится в волне одна (обоснование — ниже, в шаге 4). Решается здесь, на планировании, а не во время прогона: состав волны определяется сейчас, а профиль ревью сабагент выберет только внутри задачи, и ключевать волну на ещё не сделанный выбор нельзя. Триггеры — по фактам о задаче, каждый сам по себе достаточен:

    • трогает схему хранилища, миграцию, формат на диске или объём хранимого;
    • трогает конкурентность: транзакции, блокировки, фоновые циклы, общее состояние;
    • трогает размер тела, буфер, память, сжатие, ретеншен, темп потока;
    • её тема названа в разделах ## Прод и поток или ## Прецеденты брифа как место, где уже мерили или уже ломалось.

    Ни один триггер не сработал — задача не замеряющая, даже если её ревью окажется deep. deep про глубину проверки, замеряющая — про соревнование за железо; это разные вопросы, и совпадают они не всегда;

  • нумерованные артефакты — номера раздаёт оркестратор заранее. Если проект нумерует миграции или подобные файлы (путь — из брифа), посмотри последний номер и раздай номера всем задачам, которые, вероятно, их добавят, до запуска. Номер уходит в charter сабагента, и он берёт назначенный, а не «следующий свободный».

    Это отдельная механика от правила волны, и она ему не служит — их раньше путали, и они тянули в разные стороны. Правило волны отвечает на вопрос «кто с кем гонится одновременно», предраздача — на вопрос «какой номер берёт задача». Раздача нужна там, где две задачи одной под-пачки добавляют нумерованный артефакт: каждая считает «следующий свободный» по основной ветке, которая ещё не видела соседку, и обе берут один номер. Миграции под это почти не попадают — миграция и так триггер замеряющей задачи, а замеряющая идёт одна; но нумерованные артефакты бывают не только миграциями. Поэтому номера раздаются всем задачам с таким артефактом, независимо от того, в какой волне они окажутся: раздача ничего не стоит, а её отсутствие ловится только конфликтом на интеграции. Между волнами проблемы нет — ветка следующей волны берётся от вершины, уже включающей предыдущие;

  • жёстко сериализуем (не гоняем одновременно) настоящие пересечения:

    • одна capability на несколько задач — две задачи, правящие одну спеку (тем более одно и то же ### Requirement), дают не текстовый, а семантический конфликт при архивации; сериализуем по смыслу, а не только по файлам;
    • пересечение по одним и тем же исходникам;
  • мягкие конфликты сериализовать не надо: файлы-перечни, где каждая задача правит свою строку (индексы, оглавления, списки записей), и спеки разных capability — разные строки и файлы, сливаются сами.

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

3. Свежая база

Убедись, что рабочее дерево чистое и основная ветка свежая. Зафиксируй базовый коммит. Новые ветки бери от свежей вершины; ветки следующей волны — от вершины, уже включающей результат предыдущих волн.

4. Прогнать волны

Потолок параллелизма — 2–3 задачи одновременно. Каждая задача тянет полный цикл пайплайна с вложенным ревью и гейтом, поэтому больше трёх разом душат машину и провоцируют гонки. Волну шире трёх бей на под-пачки по ≤3 и гони под-пачки последовательно: следующая стартует, когда предыдущая вернула отчёты. Иначе потолок обходится тривиально — шесть задач, запущенных «двумя под-пачками» в одном сообщении, это шесть задач разом.

Волна из одной задачи — не вырожденный случай, а обязательный. Задача, признанная на шаге 2 замеряющей, гонится в волне одна: соседний прогон на той же машине портит числа, а находка с испорченным оракулом хуже отсутствующей — она выглядит доказанной. Если замеряющая задача всё же пошла в общей волне, её отчёт обязан нести строку в границах покрытия: замеры сняты под соседней нагрузкой.

Для каждой задачи в под-пачке:

  1. Заведи worktree и ветку от текущей вершины: git worktree add <path> -b task/<slug> <основная ветка>. Путь — во временном каталоге проекта (./tmp), не в системном /tmp.
  2. Запусти по одному сабагенту на задачу, все в одном сообщении, subagent_type: general-purpose. Charter сабагента:
    • работай строго в своём worktree <path>; в другие каталоги и в основную ветку не лезь;
    • прогони Skill av-dev-pipeline:task-pipeline ровно на этой задаче, полный цикл SDD с обоими чекпоинтами ревью;
    • если задаче назначен номер артефакта — используй строго его;
    • профиль ревью выбирается по факту изменения. Батч не повод понижать профиль: «нас много и мы спешим» — это ровно тот стимул, из-за которого проходы пропускают;
    • режим прогона проходов — последовательный. Твой worktree не один на машине;
    • если вложенные сабагенты недоступны (движок не даёт запускать агентов из агента) — не пропускай ревью и не понижай профиль: проведи его инлайн по тем же charter'ам av-dev-pipeline, сохранив обязательное — гейт до опиниативных проходов, состав по профилю, триаж последним. И скажи в отчёте прямым текстом, что ревью шло инлайн: инлайновый проход видит контекст автора и потому декоррелирован слабее — это меняет доверие к результату, а не только способ запуска;
    • вернуть отчёт, в котором обязательно: исход задачи одним из трёх слов; объявленный профиль ревью и режим прогона; что сделано; какие вопросы записаны и куда; изменённые файлы; добавлялся ли нумерованный артефакт и с каким номером; затронутые capability; состояние гейта; перечень запущенных проходов ревью поимённо с исходом каждого; путь к сохранённому отчёту триажа (openspec/changes/<id>/review/); шло ли ревью инлайн; границы покрытия.

Сабагент, упершийся в вопрос, не останавливает батч: он записывает вопрос, режет задачу до остатка и доводит остаток — либо, если остатка нет, возвращает исход «не доведена». Оркестратор собирает такие вопросы и выносит их в финальный доклад пачкой.

5. Проверить полноту ревью — до интеграции

Ветка, чей отчёт не называет профиль и проходы поимённо, не вливается. Пропуск прохода не отличим от прохода без находок, и на уровне батча это ещё опаснее: отчётов много, каждый выглядит полным, а сверять их некому, кроме тебя.

Сверка идёт в три шага, и порядок важен:

  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 -C <path> rebase <основная> — перенос ветки задачи на текущую вершину, выполняется в её собственном worktree;
  • резолв конфликтов (их почти нет — конфликтоопасное сериализовано, номера розданы заранее). Неавтоматический конфликт — не форсируй: прерви (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> — в этом порядке, иначе ветка снова занята.

Политика частичного провала. Упавшая задача (исход «не доведена», красные тесты в её worktree, конфликт при rebase, невлитая из-за находок дозапуска) не блокирует остальные: интегрируем все зелёные, упавшую оставляем в её worktree и ветке нетронутой — ничего не удаляем, — и перечисляем в докладе с причиной, отчётом и путём к worktree. Причина называется настоящая: «конфликт rebase в файле X», а не «нераспознанное пересечение» на всякий случай.

7. Финальный гейт

На основной ветке после всех интеграций — гейт целиком. Зелёное обязательно; пока красное, шаг 8 не начинается.

8. Финальная сверка — только то, чего не видел никто

Каждая задача уже прошла полный конвейер в своём worktree. Повторять его на интегрированном диффе бессмысленно: те же проходы на тех же файлах дадут те же находки и удорожат триаж. Здесь проверяется только то, что появилось от слияния:

  • запусти по одному review-specs на каждую затронутую capability. Набор назван поимённо и замеров эти проходы не делают, поэтому их допустимо гнать одним сообщением — это то самое отступление от последовательного режима, которое правило разрешает. Задание сузь до стыков: не сверять capability целиком заново, а искать рассинхрон код↔спека, возникший от слияния — требование, которое одна задача выполнила, а соседняя незаметно отменила; два change, по-разному описавшие одно поведение;
  • если задачи пересекались по файлам, добавь один review-architecture на интегрированный дифф с вопросом «не появился ли второй способ делать то, что уже делается» — именно он возникает, когда две задачи независимо решали похожее.

Замечания отрабатывай как одиночный пайплайн: инлайн чини сам, развилка — вопросом в запись; после правок — снова гейт.

9. Прибраться и доложить

  • Убери worktree и ветки только успешно влитых задач, в конце git worktree prune. Worktree и ветки провалившихся не трогай — они нужны для ручного дожатия.
  • Задачи батч не закрывает — ни одну, ни свои, ни чужие записи учёта не трогает. Он сообщает исход по каждой; закрытие происходит после приёмки и делается владельцем спринта.
  • Доложи кратко:
    • исход по каждой задаче одним из трёх слов, с хешем коммита;
    • план волн и порядок интеграции, с пометкой, какие задачи шли по одной как замеряющие;
    • вопросы, записанные сабагентами, пачкой;
    • что дозапускалось на шаге 5 и почему; шло ли где-то ревью инлайн;
    • итог финальной сверки и ссылки на архивные change;
    • Урожай — отложенные находки всех задач одним списком, с провенансом. Задачи из него заводит тот, кто ведёт задачи проекта, а не батч;
    • отдельно — провалившиеся задачи с настоящей причиной и путём к оставленному worktree;
    • границы покрытия сводной строкой, включая задачи, чьи замеры снимались в общей волне, и ветки, где ревью шло инлайн.

Тонкости

  • Изоляция параллельных тестов. Прежде чем гнать несколько прогонов разом, убедись, что тесты не делят фиксированный порт или файл БД (обычно берут временный каталог и эфемерный порт — тогда ок). Делят — гони такие задачи последовательно.
  • Поведенческая верификация внутри сабагента поднимает изменение вживую: следи, чтобы соседние worktree не дрались за порты и рабочие каталоги. Если проект умеет поднимать только один экземпляр — такие задачи в одну волну не ставь.
  • Ревью выполненного — до интеграции; это забота av-dev-pipeline:task-pipeline внутри каждого сабагента, дублировать не надо.
  • openspec validate --strict тоже внутри пайплайна задачи — не пропускай его своими правками на интеграции.
  • Крупная переработка, предложенная ревью внутри задачи, — развилка: не вливай молча, вынеси в доклад.
  • Держи вызывающего в цикле короткими репликами на переходах фаз (план → волны → интеграция → финальная сверка), но не проси подтверждать механику.