Files
dev-skills/av-dev-pipeline/skills/task-batch/SKILL.md
T
avandClaude Opus 5 d5bee11a6b классификация задачи: три категории документов и метка вместо ступени
Канон 5 объявил «каждый документ docs/ — тема ревью». Правило верно ровно
наполовину и потому вредно целиком. Паспорт и схему хранилища ревью читает, но
темами они не являются: по ним нельзя сказать «в этом изменении сделано не так»,
они задают границу, по которой судит чужая тема. Журнал решений и журнал
наблюдений ревью изменения не нужны вовсе — ADR объясняет прошлое, а не
предъявляет требование. Разметчик, применявший правило буквально, обязан был
либо завести фантомные темы passport, adr, database, research и продублировать
ими работу architecture и operations, либо потерять четыре документа молча;
случались обе ветки, и в собственном образце плана docs/passport.md не попадал
ни строкой, а обязательная арифметика покрытия при этом не сходилась.

Категорий теперь три, разрез проверяемый. Тема — да, прямо: conventions,
security, architecture и любой свой документ проекта. Источник темы — нет, но он
задаёт границу для чужой: passport, database, CLAUDE.md, openspec/specs.
Процессный — нет, он про то, как мы работаем: tasks, review, adr, research,
.pm.json. Открыта одна категория из трёх, две другие перечислены поимённо, так
что документ вне раскладки — однозначно своя тема. adr и research прогон больше
не открывает ни одним проходом; docs/review остаётся читаемым, но как настройка
конвейера, а не критерий. Цена записана и стала обязательной строкой границ
покрытия: расхождение с записанным решением ловит теперь только сверка
документации, а число под находкой обязано быть снято на этом прогоне, с
приложенной командой.

Классификация выдаёт задаче метку — small, medium, large. Прежние quick,
standard и wide назывались ступенью и описывали ревью: как глубоко смотрим.
Классифицируется же задача, и пока величина называлась свойством прогона, её
естественно было пересчитывать на каждом прогоне — что конвейер и делал. Слово
«ступень» удалено, а не оставлено синонимом: два имени одной вещи расходятся.
Выводится метка из двух разведённых осей — размер (малое, среднее, крупное) и
сложность (знакомое, незнакомое), — и равна максимуму по ним. Метка не синоним
размера: малое незнакомое изменение получает large, трогая один узел, поэтому
план печатает три строки с обоснованием каждая и выводить одну из другой
запрещено. Оси остались русскими словами — это суждение прозой; метка
английская — это идентификатор, который проходы сравнивают.

Разметка переехала из ревью кода в шаг 4 пайплайна, сразу после propose. Она
шла первым проходом каждого ревью кода, а перед ревью дизайна ту же величину
называл сам пайплайн — то есть оркестратор, который только что довёл
предложение до propose. Одно и то же измерялось дважды, и один из двух раз без
разведённости с автором, ровно в той точке, ради которой разметчик заведён.
Теперь запуск один на задачу, диффа он не видит, план обслуживает обе стадии, и
метка после кода не пересматривается: расхождение факта с разметкой ловит журнал
дефектов постфактум, как и всякую другую ошибку выбора. На диск план не пишется —
четвёртый артефакт рядом с proposal, tasks и design пережил бы задачу и разошёлся
бы с ней молча.

Ревью дизайна тоже растёт меткой: small — specs, medium — плюс rubric, large —
плюс architecture и вопрос автору о трёх формах решения. Раньше rubric и
architecture включались одним условием, и medium получал ровно один проход, то
есть не отличался от quick ничем. Разведены они потому, что зарабатывают на
разном: рубрика порождает свойства узла и окупается уже на среднем изменении,
её выход уезжает приёмочными критериями в tasks.md; архитектура отвечает на
вопрос про второй способ, а он на среднем знакомом изменении отвечается «нет»
ещё до запуска.

small подешевел тремя способами сразу. Составом: приёмник тем не запускается,
три темы ядра переходят к code сверкой по записанным инвариантам CLAUDE.md с
потолком в одну находку, и это не «глубина ниже», а другой дом темы. Входом:
specs читает только дельта-спеку, code — только индекс конвенций. Потолком: он
появился у каждого опиниативного прохода, а не у одного basics, и у половин code
он раздельный, потому что конвенционных находок больше по построению и в общем
списке они вытеснили бы техническую половину. Сработавший потолок обязан быть
объявлен строкой — молчащий срез неотличим от «больше не нашлось». Отрицательный
тест small от этого стал жёстче, а не мягче: вопросы про обратимость миграции
задавал приёмник тем, и на этой метке их не задаст никто.

Пайплайн задачи вырос до двенадцати шагов. Тривиальность перестала решать состав
ревью — она влияет только на explore; глубину обеих стадий называет метка.

Проверено прогоном ревьюверов по готовому результату: девять расхождений найдено
и починено — контракт находок печатал старый перечень проходов вместо плана по
темам, три ссылки в task-batch указывали на шаг коммита вместо закрытия, запись
changelog не переводила вопросы, адресованные passport и database, ops и
adversary утверждали, что на нижних метках их вопросы задаёт basics, шаблон
покрытия в review-code зашивал потолки small намертво, триггеры метки рассыпались
на два списка против трёх, тема из директивы CLAUDE.md могла остаться без запуска
исполнителя. Гейт зелёный: фронтматтеры, копии, одиннадцать диаграмм, ruff,
pyrefly; docs.py прогнан на живом фикстуре и печатает категорию в отказе.

Канон повышен до версии 6 с записью, выполнимой upgrade. Решения — 40–44.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 10:57:15 +03:00

45 KiB
Raw Blame History

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

Батч задач

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

По умолчанию задачи идут по одной, в порядке зависимостей. Параллельно — по явной просьбе, и тогда параллельность по графу зависимостей: одновременно гонится только то, между чем нет ни зависимости, ни пересечения. Правило и его цена — в шаге 4.

Работай максимально автономно, по тому же принципу, что и одиночный пайплайн: вопрос, который решать не тебе, записывается и не останавливает поток; спрашиваем только про необратимое (деплой, выкладка наружу, удаление или перезапись рабочих данных). Механику — планирование, worktree, rebase, интеграцию, чистку — делаем без спроса.

Предпосылки

  • OpenSpec и скиллы opsx:* — на них стоит цикл внутри каждого сабагента и проход review-specs финальной сверки. Проекта без OpenSpec это касается так же, как одиночного пайплайна (см. его раздел «Предпосылки»).
  • Скиллы зовутся с пространством имён: av-dev-pipeline:task-pipeline, av-dev-pipeline:review-pipeline, av-dev-pm:tasks. Короткое имя может разрешиться в устаревшую проектную копию, и это произойдёт молча — в charter'е сабагента пиши полное имя, он твоего контекста не видит.
  • Проектные копии этих скиллов и агентов при установке плагина удаляются.

Перед стартом прочитай CLAUDE.md проекта: оттуда берутся имя основной ветки (оно подставляется в каждую команду git ниже), команда и семантика гейта, инварианты и что запускать запрещено. Раскладка нумерованных артефактов — docs/database.md и docs/.pm.json (ключ migrations).

Документов канона нет — проект к нему не приведён. Скажи это строкой и предложи av-dev-pm:canon до первой задачи: иначе каждая задача батча заплатит поразрядной деградацией ревью, а имя основной ветки придётся угадывать.

Границы

  • Набор задач приходит извне. Батч его не формирует: не выбирает из беклога, не приоритизирует, не решает, что важнее. Набор не задан — попроси его у вызывающего и остановись.
  • Батч не владеет спринтом и целями. Он сообщает исход по каждой задаче в тех же трёх словах, что и task-pipeline: сделана / не доведена / оказалась крупнее задачи.
  • Задачи закрывает пайплайн внутри каждого сабагента, шагом 12 — после коммита работы и отдельным коммитом учёта, вызовом Skill av-dev-pm:tasks. Батч сам записей учёта не трогает: он не знает, чем кончилась приёмка, и дублировать закрытие ему незачем. Но грязное дерево после сабагента — его проблема: на нём откажут и rebase, и worktree remove (см. шаг 6). Урожай ревью батч отдаёт списком, а задачи из него заводит тот, кто ведёт задачи проекта.

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

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

Изоляция нужна в обоих режимах, а не только в параллельном: батч не вливает ветку, пока не проверил полноту её ревью (шаг 5), и упавшая задача обязана остаться в своём worktree для ручного дожатия (шаг 6), не оставив следа в основной ветке. В параллельном режиме к этому добавляется вторая причина — задачи не должны видеть недоделанную работу друг друга.

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

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

Шаги

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

Набор задан списком (слаги, файлы, описания) — прочитай файл каждой задачи и связанные спеки и черновики. Сырьё (в терминах av-dev-pm — запись типа research с пустым разделом «Вопрос») включается, но помни: сабагент проведёт его сперва через opsx:explore, это тяжелее и чаще упирается в вопрос.

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

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

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

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

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

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

    Ни один триггер не сработал — задача не замеряющая, даже если её ревью окажется large. Метка про глубину проверки, замеряющая — про соревнование за железо; это разные вопросы, и совпадают они не всегда. Обратное тоже бывает и тоже законно: помеченная задача, чьё ревью пошло меткой small или medium, машину не займёт вовсе — меряющие проходы живут только в large. Пометка от этого не снимается: она ставится до разметки, и перестраховка здесь стоит одной волны, а ошибка — испорченных чисел;

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

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

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

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

Собери план как граф зависимостей, а не как плоский список: рёбра — жёсткие зависимости и сериализуемые пересечения. Дальше по режиму:

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

Рёбра у графа двух видов, и путать их не надо: зависимость направлена (B без результата A не делается), пересечение — нет (кто первый, неважно, лишь бы не разом). Тот же словарь у графа проходов ревью — см. «Порядок прогона» в av-dev-pipeline:review-pipeline.

flowchart TD
    A["A: схема хранилища<br/>(замеряющая)"]
    B["B: эндпоинт поверх A"]
    C["C: формат лога"]
    D["D: правит ту же capability, что C"]

    A -->|зависимость| B
    C -. пересечение — одна capability .- D

Этот граф даёт: последовательноA → B → C → D (или A → C → B → D, обе линеаризации законны); параллельно — волна 1 A одна (замеряющая), волна 2 B и C, волна 3 D.

Схемы в этом скилле — пример и сводка, правила ставит текст: при расхождении прав он. (В av-dev-pipeline:review-pipeline наоборот — там граф прогона и есть алгоритм, и старший он.)

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

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

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

4. Провести задачи

Умолчание — по одной задаче за раз, в порядке из шага 2. Следующая стартует, когда предыдущая вернула отчёт и (если она зелёная) влилась. Обосновывать это не надо — обосновывается отступление. Причина умолчания в том, что задача батча дороже прохода ревью: каждая тянет полный цикл пайплайна с гейтом, поднятием сервиса и вложенным ревью, и две такие на одной машине дерутся за порты, рабочие каталоги, СУБД и само железо. Последовательный прогон к тому же оставляет ревью внутри задачи его собственное умолчание — параллельные проходы: машина свободна, и выигрыш берётся там, где он ничего не стоит.

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

В параллельном режиме действуют два ограничения:

  • потолок — 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 с обоими чекпоинтами ревью;
    • если задаче назначен номер артефакта — используй строго его;
    • метка ревью выбирает разметчик конвейера, а не ты и не сабагент. Он идёт внутри пайплайна задачи, шагом 4, сразу после propose, и его план обслуживает оба чекпоинта. Метка в вызов не передаётся вовсе. Батч не повод её понижать: «нас много и мы спешим» — ровно тот стимул, из-за которого проходы пропускают, и он снят тем, что регулятор не в руках у автора;
    • режим прогона проходов ревью — от режима батча, и его называет charter, а не сабагент: батч идёт по одной задаче → режим умолчательный, по графу (машина свободна); батч идёт волнами → линейно, твой worktree не один на машине, и этой причиной ты обязан объяснить режим в отчёте. Внутренние рёбра графа — цепочку проходов, держащих машину — конвейер соблюдает сам, в любом режиме;
    • если вложенные сабагенты недоступны (движок не даёт запускать агентов из агента) — не пропускай ревью и не понижай метка: проведи его инлайн по тем же charter'ам av-dev-pipeline, сохранив обязательное — разметку первой (план с темами и меткой), гейт до опиниативных проходов, состав по плану, триаж последним. И скажи в отчёте прямым текстом, что ревью шло инлайн: инлайновый проход видит контекст автора и потому разведён с ним слабее — а инлайновая разметка вдобавок означает, что метка выбрал автор, и это отдельная строка;
    • вернуть отчёт, в котором обязательно: исход задачи одним из трёх слов; план прогона: метка с обоснованием, темы и их глубины, и режим; что сделано; какие вопросы записаны и куда; изменённые файлы; добавлялся ли нумерованный артефакт и с каким номером; затронутые capability; состояние гейта; перечень тем с исходом по каждой; путь к сохранённому отчёту триажа (openspec/changes/<id>/review/, после архивации — openspec/changes/archive/<id>/review/); шло ли ревью инлайн; границы покрытия.

Шаги 5 и 6 отрабатываются на вернувшейся задаче до старта следующей — в параллельном режиме на вернувшейся волне до старта следующей. Иначе ветка следующей возьмётся от вершины, не видевшей предыдущую работу, и весь смысл порядка из шага 2 теряется.

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

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

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

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

  1. Возьми план прогона из отчёта задачи — таблицу «тема → дом → глубина → кто закрывает» с меткой и обоснованием; он затем и заказан в обязательных полях шага 4. Плана в отчёте нет — сверять не с чем; это само по себе основание не вливать, пока сабагент не покажет план разметки задачи.
  2. Сверяй с независимым артефактом, а не с прозой отчёта. Перечень проходов бери из сохранённого отчёта триажа (openspec/changes/<id>/review/ или openspec/changes/archive/<id>/review/ — задача доведена, change заархивирован) — пайплайн обязан его туда положить. Проза сабагента написана тем же, кто мог проход и пропустить: она подтверждает сама себя. Отчёта триажа на месте нет — считай, что состав неизвестен, и дозапускай ревью целиком.
  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 как есть, вынеси это в доклад как нераспознанное пересечение;
  • дерево сабагента обязано быть чистым. git -C <path> status --porcelain до rebase: непусто — значит сабагент не довёл шаг 12 до коммита учёта (или оставил мусор). Не форсируй и не коммить за него: назови задачу в докладе недоведённой и оставь ветку с worktree. Молчаливый rebase на грязном дереве всё равно откажет, но с сообщением про unstaged changes — а причина другая;
  • ненулевой код 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, все разом — граф здесь плоский: машину эти проходы не держат, ребра между ними нет, а сама машина к этому моменту свободна (все сабагенты вернулись). Линейно — только по причине из раздела «Порядок прогона» av-dev-pipeline:review-pipeline, и причину назови;

  • задание у этих проходов особое, и это надо сказать прямо. Живого change здесь нет — все заархивированы, дельта-спек не существует. Источник требований — актуальные openspec/specs/<capability>/spec.md, а предмет — стык: требование, которое одна задача выполнила, а соседняя незаметно отменила; два архивных change, по-разному описавшие одно поведение. Скажи проходу это прямо, иначе он пойдёт искать дельты и не найдёт ничего;

  • если задачи пересекались по файлам, добавь один review-architecture на интегрированный дифф с вопросом «не появился ли второй способ делать то, что уже делается» — именно он возникает, когда две задачи независимо решали похожее;

  • заверши триажем. Он единственный, кто агрегирует, и без него у находок нет ни оракула, ни пометки инлайн/развилка — а следующий абзац на неё опирается. Прогон из двух проходов без триажа — это сырые находки, выданные за разобранные;

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

    метка:   не применяется — сверка стыка, а не ревью изменения
    тема       дом                                  глубина  закрывает
    requirements  openspec/specs/<capability-1>/    разбор   specs (стык)
    requirements  openspec/specs/<capability-2>/    разбор   specs (стык)
    architecture  docs/architecture.md              разбор   architecture
                  + источник docs/passport.md
    

    Темы, которых в этом списке нет (autotests, conventions, security, operations, свои темы проекта), назови строкой «не проверяется на сверке стыка: закрыто прогонами отдельных задач». Это не формальность — без такой строки отчёт сверки читается как полное ревью ветки.

Граф этой сверки — веер в один сток, и он такой же, как у обычного прогона:

flowchart TD
    merged["основная ветка после всех интеграций<br/>(финальный гейт зелёный)"]
    s1["review-specs: capability 1<br/>режим «стык после слияния»"]
    s2["review-specs: capability 2<br/>режим «стык после слияния»"]
    arch["review-architecture на интегрированном диффе<br/>(если задачи пересекались по файлам)"]
    tri["review-triage — сток"]

    merged --> s1 --> tri
    merged --> s2 --> tri
    merged --> arch --> tri

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

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

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

Тонкости

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