режимы прогона: ревью параллельно, батч — по одной задаче
Умолчания разошлись по цене шага. Проход ревью читает и рассуждает: он ничего не поднимает, ни за что не дерётся и по построению не видит выводов соседа — очередь между проходами добавляет только ожидание. Задача батча тянет полный цикл пайплайна с гейтом, поднятием сервиса и вложенным ревью — две такие дерутся за порты, каталоги и железо. review-pipeline: параллельно внутри стадии — умолчание. Последовательно — по трём причинам с именем в отчёте: сказал оператор, проходы меряют, машина занята. Просьба «последовательно» набора не требует. Меряющая пара adversary + ops стала именованным исключением: она идёт по очереди всегда, и общее «гони параллельно» этого не отменяет. Ранний выход переехал на границу стадии. task-batch: план собирается графом зависимостей и в умолчании линеаризуется. Параллельно — по просьбе, и просьба разрешает ширину графа, а не «всё разом»; потолок 2–3 и одиночная волна замеряющей задачи сохранены как правила этого режима. Режим ревью внутри задачи выводится из режима батча и называется в charter'е. Финальная сверка гонит review-specs по capability параллельно. DECISIONS 14 — с причиной; версия канона не меняется, канон этих скиллов не описывает. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: task-batch
|
||||
description: Проводит несколько задач разом — планирует порядок и пересечения, гонит каждую задачу отдельным сабагентом в своём git worktree через task-pipeline, интегрирует по одной ветке через rebase + fast-forward (линейная история), проверяет полноту ревью каждой ветки и в конце сверяет стыки, возникшие от слияния. Набор задач приходит извне. Использовать, когда просят сделать несколько задач сразу.
|
||||
description: Проводит несколько задач разом — планирует порядок и пересечения, гонит каждую задачу отдельным сабагентом в своём git worktree через task-pipeline (по умолчанию по одной задаче за раз; параллельно по графу зависимостей — по явной просьбе), интегрирует по одной ветке через rebase + fast-forward (линейная история), проверяет полноту ревью каждой ветки и в конце сверяет стыки, возникшие от слияния. Набор задач приходит извне. Использовать, когда просят сделать несколько задач сразу.
|
||||
---
|
||||
|
||||
# Батч задач
|
||||
@@ -11,6 +11,11 @@ description: Проводит несколько задач разом — пл
|
||||
и делает финальную сверку. Тонкая обёртка над пайплайном задачи — не
|
||||
переизобретай её шаги, вызывай как есть.
|
||||
|
||||
**По умолчанию задачи идут по одной**, в порядке зависимостей. Параллельно — по
|
||||
явной просьбе, и тогда параллельность **по графу зависимостей**: одновременно
|
||||
гонится только то, между чем нет ни зависимости, ни пересечения. Правило и его
|
||||
цена — в шаге 4.
|
||||
|
||||
Работай **максимально автономно**, по тому же принципу, что и одиночный пайплайн:
|
||||
вопрос, который решать не тебе, записывается и не останавливает поток; спрашиваем
|
||||
только про **необратимое** (деплой, выкладка наружу, удаление или перезапись
|
||||
@@ -34,7 +39,7 @@ description: Проводит несколько задач разом — пл
|
||||
`docs/database.md` и `docs/.pm.json` (ключ `migrations`).
|
||||
|
||||
**Документов канона нет — проект к нему не приведён.** Скажи это строкой и
|
||||
предложи `av-dev-pm:canon` **до первой волны**: иначе каждая задача батча
|
||||
предложи `av-dev-pm:canon` **до первой задачи**: иначе каждая задача батча
|
||||
заплатит поразрядной деградацией ревью, а имя основной ветки придётся
|
||||
угадывать.
|
||||
|
||||
@@ -56,10 +61,16 @@ description: Проводит несколько задач разом — пл
|
||||
## Ключевое отличие от одиночного пайплайна
|
||||
|
||||
`task-pipeline` коммитит **в текущую ветку**, и при ручном запуске это основная
|
||||
ветка. Здесь так нельзя для параллельных задач, поэтому батч — **осознанное
|
||||
исключение**: временные ветки и worktree заводятся лишь как средство изоляции, а
|
||||
конечное состояние — та же линейная история основной ветки через rebase +
|
||||
fast-forward. Ветки после вливания удаляются.
|
||||
ветка. Здесь так нельзя, поэтому батч — **осознанное исключение**: временные
|
||||
ветки и worktree заводятся лишь как средство изоляции, а конечное состояние — та
|
||||
же линейная история основной ветки через rebase + fast-forward. Ветки после
|
||||
вливания удаляются.
|
||||
|
||||
Изоляция нужна **в обоих режимах, а не только в параллельном**: батч не вливает
|
||||
ветку, пока не проверил полноту её ревью (шаг 5), и упавшая задача обязана
|
||||
остаться в своём worktree для ручного дожатия (шаг 6), не оставив следа в
|
||||
основной ветке. В параллельном режиме к этому добавляется вторая причина —
|
||||
задачи не должны видеть недоделанную работу друг друга.
|
||||
|
||||
## Модель исполнения
|
||||
|
||||
@@ -87,10 +98,12 @@ fast-forward. Ветки после вливания удаляются.
|
||||
- **затронутые capability** — по её описанию и по каталогу актуальных спек
|
||||
(`openspec/specs/`);
|
||||
- **жёсткие зависимости**: задача B строится на результате A → A строго раньше B;
|
||||
- **замеряющая задача** — та, чьё ревью будет доказывать находки **числами**, и
|
||||
потому она гонится в волне **одна** (обоснование — ниже, в шаге 4). Решается
|
||||
здесь, на планировании, а не во время прогона: состав волны определяется
|
||||
сейчас, а профиль ревью сабагент выберет только внутри задачи, и ключевать
|
||||
- **замеряющая задача** — та, чьё ревью будет доказывать находки **числами**. В
|
||||
последовательном прогоне это ничего не меняет: она и так идёт одна. В
|
||||
параллельном она гонится в волне **одна** (обоснование — ниже, в шаге 4).
|
||||
Помечается здесь, на планировании, а не во время прогона, и **независимо от
|
||||
режима**: состав волны определяется сейчас, режим может смениться просьбой уже
|
||||
после плана, а профиль ревью сабагент выберет только внутри задачи — ключевать
|
||||
волну на ещё не сделанный выбор нельзя. Триггеры — по фактам о задаче, каждый
|
||||
сам по себе достаточен:
|
||||
- трогает схему хранилища, миграцию, формат на диске или объём хранимого;
|
||||
@@ -120,7 +133,9 @@ fast-forward. Ветки после вливания удаляются.
|
||||
**всем** задачам с таким артефактом, независимо от того, в какой волне они
|
||||
окажутся: раздача ничего не стоит, а её отсутствие ловится только конфликтом на
|
||||
интеграции. Между волнами проблемы нет — ветка следующей волны берётся от
|
||||
вершины, уже включающей предыдущие;
|
||||
вершины, уже включающей предыдущие; в последовательном прогоне проблемы нет по
|
||||
той же причине, но номера всё равно раздай: режим может смениться просьбой, а
|
||||
раздача бесплатна;
|
||||
- **жёстко сериализуем** (не гоняем одновременно) настоящие пересечения:
|
||||
- **одна capability на несколько задач** — две задачи, правящие одну спеку (тем
|
||||
более одно и то же `### Requirement`), дают не текстовый, а **семантический**
|
||||
@@ -130,39 +145,65 @@ fast-forward. Ветки после вливания удаляются.
|
||||
правит **свою** строку (индексы, оглавления, списки записей), и спеки разных
|
||||
capability — разные строки и файлы, сливаются сами.
|
||||
|
||||
Собери план: **волны** параллельно-безопасных задач плюс сериализованный хвост
|
||||
конфликтоопасных, с учётом зависимостей; замеряющие задачи стоят в плане
|
||||
отдельными волнами по одной. Покажи план короткой репликой — назвав, какие
|
||||
задачи признаны замеряющими и по какому триггеру, — и иди дальше.
|
||||
Собери план как **граф зависимостей**, а не как плоский список: рёбра — жёсткие
|
||||
зависимости и сериализуемые пересечения. Дальше по режиму:
|
||||
|
||||
- **последовательно (умолчание)** — линеаризуй граф в один порядок:
|
||||
топологический, а там, где он оставляет свободу, — раньше то, от чего зависит
|
||||
больше задач, и раньше то, что правит общие для набора места. Волн нет,
|
||||
замеряющие задачи ничем не отличаются от прочих;
|
||||
- **параллельно (по просьбе)** — нарежь граф на **волны**: в одну волну попадают
|
||||
только задачи, между которыми нет ребра; замеряющие стоят отдельными волнами по
|
||||
одной.
|
||||
|
||||
Покажи план короткой репликой — режим, порядок или состав волн, какие задачи
|
||||
признаны замеряющими и по какому триггеру, — и иди дальше.
|
||||
|
||||
### 3. Свежая база
|
||||
|
||||
Убедись, что рабочее дерево чистое и основная ветка свежая. Зафиксируй базовый
|
||||
коммит. Новые ветки бери от свежей вершины; ветки следующей волны — от вершины,
|
||||
уже включающей результат предыдущих волн.
|
||||
коммит. Новые ветки бери от свежей вершины; ветку следующей задачи (в
|
||||
параллельном режиме — ветки следующей волны) — от вершины, уже включающей
|
||||
результат предыдущих.
|
||||
|
||||
### 4. Прогнать волны
|
||||
### 4. Провести задачи
|
||||
|
||||
**Потолок параллелизма — 2–3 задачи одновременно.** Каждая задача тянет полный
|
||||
цикл пайплайна с вложенным ревью и гейтом, поэтому больше трёх разом душат
|
||||
машину и провоцируют гонки. Волну шире трёх бей на под-пачки по ≤3 и **гони
|
||||
под-пачки последовательно**: следующая стартует, когда предыдущая вернула отчёты.
|
||||
Иначе потолок обходится тривиально — шесть задач, запущенных «двумя под-пачками»
|
||||
в одном сообщении, это шесть задач разом.
|
||||
**Умолчание — по одной задаче за раз, в порядке из шага 2.** Следующая стартует,
|
||||
когда предыдущая вернула отчёт и (если она зелёная) влилась. Обосновывать это не
|
||||
надо — обосновывается отступление. Причина умолчания в том, что задача батча
|
||||
дороже прохода ревью: каждая тянет полный цикл пайплайна с гейтом, поднятием
|
||||
сервиса и вложенным ревью, и две такие на одной машине дерутся за порты, рабочие
|
||||
каталоги, СУБД и само железо. Последовательный прогон к тому же оставляет ревью
|
||||
внутри задачи его собственное умолчание — **параллельные проходы**: машина
|
||||
свободна, и выигрыш берётся там, где он ничего не стоит.
|
||||
|
||||
**Волна из одной задачи — не вырожденный случай, а обязательный.** Задача,
|
||||
признанная на шаге 2 **замеряющей**, гонится в волне одна: соседний прогон на той
|
||||
же машине портит числа, а находка с испорченным оракулом хуже отсутствующей — она
|
||||
выглядит доказанной. Если замеряющая задача всё же пошла в общей волне, её отчёт
|
||||
обязан нести строку в границах покрытия: замеры сняты под соседней нагрузкой.
|
||||
**Параллельно — по явной просьбе, и параллельность идёт по графу зависимостей.**
|
||||
Одновременно гонится только то, между чем на шаге 2 не нашлось ребра: ни жёсткой
|
||||
зависимости, ни общей capability, ни общих исходников. «Гони параллельно» не
|
||||
означает «гони всё разом» — граф остаётся в силе, просьба лишь разрешает
|
||||
использовать его ширину.
|
||||
|
||||
Для каждой задачи в под-пачке:
|
||||
В параллельном режиме действуют два ограничения:
|
||||
|
||||
- **потолок — 2–3 задачи одновременно.** Больше трёх разом душат машину и
|
||||
провоцируют гонки. Волну шире трёх бей на под-пачки по ≤3 и **гони под-пачки
|
||||
последовательно**: следующая стартует, когда предыдущая вернула отчёты. Иначе
|
||||
потолок обходится тривиально — шесть задач, запущенных «двумя под-пачками» в
|
||||
одном сообщении, это шесть задач разом;
|
||||
- **волна из одной задачи обязательна для замеряющей.** Задача, признанная на
|
||||
шаге 2 **замеряющей**, гонится одна: соседний прогон на той же машине портит
|
||||
числа, а находка с испорченным оракулом хуже отсутствующей — она выглядит
|
||||
доказанной. Если замеряющая задача всё же пошла в общей волне, её отчёт обязан
|
||||
нести строку в границах покрытия: замеры сняты под соседней нагрузкой.
|
||||
|
||||
Для каждой задачи (в параллельном режиме — для каждой задачи под-пачки):
|
||||
|
||||
1. Заведи worktree и ветку от текущей вершины:
|
||||
`git worktree add <path> -b task/<slug> <основная ветка>`. Путь — во временном
|
||||
каталоге проекта (`./tmp`), не в системном `/tmp`.
|
||||
2. Запусти **по одному сабагенту на задачу, все в одном сообщении**,
|
||||
`subagent_type: general-purpose`. Charter сабагента:
|
||||
2. Запусти сабагента, `subagent_type: general-purpose`: в последовательном режиме
|
||||
— одного и дождись отчёта; в параллельном — **по одному на задачу под-пачки,
|
||||
всех в одном сообщении**. Charter сабагента:
|
||||
- работай **строго в своём worktree** `<path>`; в другие каталоги и в основную
|
||||
ветку не лезь;
|
||||
- прогони Skill **`av-dev-pipeline:task-pipeline`** ровно на этой задаче,
|
||||
@@ -171,8 +212,12 @@ fast-forward. Ветки после вливания удаляются.
|
||||
- **профиль ревью выбирается по факту изменения.** Батч не повод понижать
|
||||
профиль: «нас много и мы спешим» — это ровно тот стимул, из-за которого
|
||||
проходы пропускают;
|
||||
- **режим прогона проходов — последовательный.** Твой worktree не один на
|
||||
машине;
|
||||
- **режим прогона проходов ревью — от режима батча**, и его называет charter,
|
||||
а не сабагент: батч идёт по одной задаче → режим умолчательный,
|
||||
**параллельный** (машина свободна); батч идёт волнами → **последовательный**,
|
||||
твой worktree не один на машине, и этой причиной ты обязан объяснить режим в
|
||||
отчёте. Меряющую пару `adversary` и `ops` конвейер держит по очереди сам, в
|
||||
любом режиме;
|
||||
- **если вложенные сабагенты недоступны** (движок не даёт запускать агентов из
|
||||
агента) — не пропускай ревью и не понижай профиль: проведи его **инлайн** по
|
||||
тем же charter'ам `av-dev-pipeline`, сохранив обязательное — гейт до
|
||||
@@ -189,6 +234,11 @@ fast-forward. Ветки после вливания удаляются.
|
||||
архивации — `openspec/changes/archive/<id>/review/`); шло ли ревью
|
||||
инлайн; границы покрытия.
|
||||
|
||||
**Шаги 5 и 6 отрабатываются на вернувшейся задаче до старта следующей** — в
|
||||
параллельном режиме на вернувшейся волне до старта следующей. Иначе ветка
|
||||
следующей возьмётся от вершины, не видевшей предыдущую работу, и весь смысл
|
||||
порядка из шага 2 теряется.
|
||||
|
||||
Сабагент, упершийся в вопрос, **не останавливает батч**: он записывает вопрос,
|
||||
режет задачу до остатка и доводит остаток — либо, если остатка нет, возвращает
|
||||
исход «не доведена». Оркестратор собирает такие вопросы и выносит их в финальный
|
||||
@@ -296,11 +346,11 @@ rebase в файле X», а не «нераспознанное пересеч
|
||||
слияния**:
|
||||
|
||||
- запусти **по одному `review-specs` на каждую затронутую capability**,
|
||||
последовательно. Параллельно — только если человек попросил явно и назвал
|
||||
набор: правило и оба его условия живут в
|
||||
`av-dev-pipeline:review-pipeline`, раздел «Режим запуска», и здесь не
|
||||
ослабляются. «Замеров не делают» основанием не является;
|
||||
- **режим у этих проходов особый, и его надо назвать в задании.** Живого change
|
||||
параллельно — как велит умолчание конвейера: замеров эти проходы не делают,
|
||||
друг другу не мешают, а машина к этому моменту свободна (все сабагенты
|
||||
вернулись). По очереди — только по особой причине из раздела «Режим запуска»
|
||||
`av-dev-pipeline:review-pipeline`, и причину назови;
|
||||
- **задание у этих проходов особое, и это надо сказать прямо.** Живого change
|
||||
здесь нет — все заархивированы, дельта-спек не существует. Источник требований
|
||||
— **актуальные** `openspec/specs/<capability>/spec.md`, а предмет — стык:
|
||||
требование, которое одна задача выполнила, а соседняя незаметно отменила; два
|
||||
@@ -329,8 +379,9 @@ rebase в файле X», а не «нераспознанное пересеч
|
||||
строкой — иначе задача останется открытой молча.
|
||||
- Доложи кратко:
|
||||
- **исход по каждой задаче** одним из трёх слов, с хешем коммита;
|
||||
- план волн и порядок интеграции, с пометкой, какие задачи шли по одной как
|
||||
замеряющие;
|
||||
- **режим прогона** — по одной или волнами, и если волнами, то по чьей просьбе;
|
||||
порядок задач или состав волн, порядок интеграции, с пометкой, какие задачи
|
||||
шли по одной как замеряющие;
|
||||
- вопросы, записанные сабагентами, пачкой;
|
||||
- что дозапускалось на шаге 5 и почему; шло ли где-то ревью инлайн;
|
||||
- итог финальной сверки и ссылки на архивные change;
|
||||
@@ -343,18 +394,22 @@ rebase в файле X», а не «нераспознанное пересеч
|
||||
|
||||
## Тонкости
|
||||
|
||||
- **Изоляция параллельных тестов.** Прежде чем гнать несколько прогонов разом,
|
||||
убедись, что тесты не делят фиксированный порт или файл БД (обычно берут
|
||||
временный каталог и эфемерный порт — тогда ок). Делят — гони такие задачи
|
||||
последовательно.
|
||||
- Поведенческая верификация внутри сабагента поднимает изменение вживую: следи,
|
||||
чтобы соседние worktree не дрались за порты и рабочие каталоги. Если проект
|
||||
умеет поднимать только один экземпляр — такие задачи в одну волну не ставь.
|
||||
- **Изоляция параллельных тестов — цена параллельного режима, и проверяется она
|
||||
до первой волны.** Прежде чем гнать несколько прогонов разом, убедись, что
|
||||
тесты не делят фиксированный порт или файл БД (обычно берут временный каталог и
|
||||
эфемерный порт — тогда ок). Делят — такие задачи гони по одной, даже если
|
||||
просили параллельно, и скажи об этом строкой: просьба про параллельность, а не
|
||||
про сломанные тесты. В умолчательном режиме вопрос не встаёт вовсе — это одна
|
||||
из причин, по которым умолчание такое.
|
||||
- Поведенческая верификация внутри сабагента поднимает изменение вживую: в
|
||||
параллельном режиме следи, чтобы соседние worktree не дрались за порты и
|
||||
рабочие каталоги. Если проект умеет поднимать только один экземпляр — такие
|
||||
задачи в одну волну не ставь.
|
||||
- Ревью выполненного — **до** интеграции; это забота
|
||||
`av-dev-pipeline:task-pipeline` внутри каждого сабагента, дублировать не надо.
|
||||
- `openspec validate --strict` тоже внутри пайплайна задачи — не пропускай его
|
||||
своими правками на интеграции.
|
||||
- Крупная переработка, предложенная ревью внутри задачи, — развилка: не вливай
|
||||
молча, вынеси в доклад.
|
||||
- Держи вызывающего в цикле короткими репликами на переходах фаз (план → волны →
|
||||
интеграция → финальная сверка), но не проси подтверждать механику.
|
||||
- Держи вызывающего в цикле короткими репликами на переходах фаз (план → прогон
|
||||
задач → интеграция → финальная сверка), но не проси подтверждать механику.
|
||||
|
||||
Reference in New Issue
Block a user