# ADR: Auto Mode — полный автопилот (KIN-012) **Дата:** 2026-03-15 **Статус:** Accepted **Автор:** architect (KIN-012) --- ## Контекст Задача: реализовать два режима исполнения пайплайнов: - **Auto** — полный автопилот: pipeline → auto-approve → auto-followup → auto-rerun при permission issues → hooks. Без остановок на review/blocked. - **Review** — текущее поведение: задача уходит в статус `review`, ждёт ручного approve. ### Что уже реализовано (анализ кода) **1. Хранение режима — `core/db.py`** - `projects.execution_mode TEXT NOT NULL DEFAULT 'review'` — дефолт на уровне проекта - `tasks.execution_mode TEXT` — nullable, переопределяет проект - Миграции добавляют оба столбца к существующим БД **2. Приоритет режима — `core/models.py:get_effective_mode()`** ``` task.execution_mode > project.execution_mode > 'review' ``` Вычисляется один раз в начале `run_pipeline`. **3. Auto-approve — `agents/runner.py:run_pipeline()`** (строки 519–536) ```python if mode == "auto": models.update_task(conn, task_id, status="done") run_hooks(conn, project_id, task_id, event="task_auto_approved", ...) else: models.update_task(conn, task_id, status="review") ``` **4. Permission retry — `agents/runner.py:run_pipeline()`** (строки 453–475) ```python if mode == "auto" and not allow_write and _is_permission_error(result): run_hooks(..., event="task_permission_retry", ...) retry = run_agent(..., allow_write=True) allow_write = True # propagates to all subsequent steps ``` - Срабатывает **только в auto режиме** - Ровно **1 попытка retry** на шаг - После первого retry `allow_write=True` сохраняется на весь оставшийся пайплайн **5. Паттерны permission errors — `core/followup.py:PERMISSION_PATTERNS`** ``` permission denied, ручное применение, cannot write, read-only, manually appl, ... ``` **6. Post-pipeline hooks — `core/hooks.py`** События: `pipeline_completed`, `task_auto_approved`, `task_permission_retry` --- ## Пробелы — что НЕ реализовано ### Gap 1: Auto-followup не вызывается из run_pipeline `generate_followups()` существует в `core/followup.py`, но нигде не вызывается автоматически. В `run_pipeline` после завершения пайплайна — только хуки. ### Gap 2: Auto-resolution pending_actions в auto mode `generate_followups()` возвращает `pending_actions` (permission-blocked followup items) с опциями `["rerun", "manual_task", "skip"]`. В auto mode нет логики автоматического выбора опции. ### Gap 3: Наследование режима followup-задачами Задачи, созданные через `generate_followups()`, создаются с `execution_mode=None` (наследуют от проекта). Это правильное поведение, но не задокументировано. --- ## Решения ### D1: Где хранить режим **Решение:** двухуровневая иерархия (уже реализована, зафиксируем). | Уровень | Поле | Дефолт | Переопределяет | |---------|------|--------|----------------| | Глобальный | — | `review` | — | | Проект | `projects.execution_mode` | `'review'` | глобальный | | Задача | `tasks.execution_mode` | `NULL` | проект | Глобального конфига нет — осознанное решение. Каждый проект управляет своим режимом. Задача может переопределить проект (например, форсировать `review` для security-sensitive задач). **Изменения БД не нужны** — структура готова. --- ### D2: Как runner обходит ожидание approve в auto mode **Решение:** уже реализовано. Зафиксируем контракт: ``` run_pipeline() в auto mode: 1. Все шаги выполняются последовательно 2. При успехе → task.status = "done" (минуя "review") 3. Хук task_auto_approved + pipeline_completed 4. generate_followups() автоматически (Gap 1, см. D4) ``` В review mode — без изменений: `task.status = "review"`, `generate_followups()` не вызывается автоматически. --- ### D3: Auto-rerun при permission issues — лимит и критерии **Что считать permission issue:** Паттерны из `PERMISSION_PATTERNS` в `core/followup.py`. Список достаточен, расширяется при необходимости через PR. **Лимит попыток:** **1 retry per step** (уже реализовано). Обоснование: - Permission issue — либо системная проблема (нет прав на директорию), либо claude CLI требует `--dangerously-skip-permissions` - Второй retry с теми же параметрами не имеет смысла — проблема детерминированная - Если 1 retry не помог → `task.status = "blocked"` даже в auto mode **Поведение после retry:** `allow_write=True` применяется ко **всем последующим шагам** пайплайна (не только retry шагу). Это безопасно в контексте Kin — агенты работают в изолированном рабочем каталоге проекта. **Хук `task_permission_retry`:** Срабатывает перед retry — позволяет логировать / оповещать, но не блокирует. **Итоговая таблица поведения при failure:** | Режим | Тип ошибки | Поведение | |-------|-----------|-----------| | auto | permission error (первый) | retry с allow_write=True | | auto | permission error (после retry) | blocked | | auto | любая другая ошибка | blocked | | review | любая ошибка | blocked | --- ### D4: Auto-followup интеграция с post-pipeline hooks **Решение:** `generate_followups()` вызывается из `run_pipeline()` в auto mode **после** `task_auto_approved` хука. Порядок событий в auto mode: ``` 1. pipeline успешно завершён 2. task.status = "done" 3. хук: task_auto_approved ← пользовательские хуки (rebuild-frontend и т.д.) 4. generate_followups() ← анализируем output, создаём followup задачи 5. хук: pipeline_completed ← финальное уведомление ``` В review mode: ``` 1. pipeline успешно завершён 2. task.status = "review" 3. хук: pipeline_completed ← generate_followups() НЕ вызывается (ждём manual approve) ``` **Почему после task_auto_approved, а не до:** Хуки типа `rebuild-frontend` (KIN-010) изменяют состояние файловой системы. Followup-агент должен видеть актуальное состояние проекта после всех хуков. --- ### D5: Auto-resolution pending_actions в auto mode `generate_followups()` может вернуть `pending_actions` — элементы, заблокированные из-за permission issues. В auto mode нужна автоматическая стратегия. **Решение:** в auto mode `pending_actions` резолвятся как `"rerun"`. Обоснование: - Auto mode = полный автопилот, пользователь не должен принимать решения - "rerun" — наиболее агрессивная и полезная стратегия: повторяем шаг с `allow_write=True` - Если rerun снова даёт permission error → создаётся manual_task (escalation) ``` auto mode + pending_action: → resolve_pending_action(choice="rerun") → если rerun провалился → create manual_task с тегом "auto_escalated" → всё логируется review mode + pending_action: → возвращается пользователю через API для ручного выбора ``` --- ### D6: Наследование режима followup-задачами Задачи, созданные через `generate_followups()`, создаются с `execution_mode=None`. **Решение:** followup-задачи наследуют режим через проект (существующая иерархия D1). Явно устанавливать `execution_mode` в followup-задачах **не нужно** — если проект в auto, все его задачи по умолчанию в auto. Исключение: если оригинальная задача была в `review` (ручной override), followup-задачи НЕ наследуют это — они создаются "чисто" от проекта. Это намеренное поведение: override в задаче — разовое действие. --- ## Итоговая карта изменений (что нужно реализовать) | # | Файл | Изменение | Gap | |---|------|----------|-----| | 1 | `agents/runner.py` | Вызов `generate_followups()` в auto mode после `task_auto_approved` | D4 | | 2 | `core/followup.py` | Auto-resolution `pending_actions` в `generate_followups()` при auto mode | D5 | | 3 | `web/api.py` | Endpoint для смены `execution_mode` проекта/задачи | — | | 4 | `web/frontend` | UI переключатель Auto/Review (project settings + task detail) | — | **Что НЕ нужно менять:** - `core/db.py` — схема готова - `core/models.py` — `get_effective_mode()` готов - `core/hooks.py` — события готовы - Permission detection в `runner.py` — готово --- ## Риски и ограничения 1. **Стоимость в auto mode**: `generate_followups()` добавляет один запуск агента после каждого пайплайна. При высокой нагрузке это существенный overhead. Митигация: `generate_followups()` можно сделать опциональным (флаг `auto_followup` в project settings). 2. **Permission retry scope**: `allow_write=True` после первого retry применяется ко всем последующим шагам. Это агрессивно, но допустимо, т.к. агент уже начал писать файлы. 3. **Infinite loop в auto-followup**: если followup создаёт задачи, а те создают ещё followup — нет механизма остановки. Митигация: `parent_task_id` позволяет отслеживать глубину. Задачи с `source: followup:*` глубже 1 уровня — не генерируют followup автоматически. 4. **Race condition**: если два пайплайна запускаются для одной задачи одновременно — БД-уровень не блокирует. SQLite WAL + `task.status = 'in_progress'` в начале пайплайна дают частичную защиту, но не полную. --- ## Статус реализации - [x] DB schema: `execution_mode` в `projects` и `tasks` - [x] `get_effective_mode()` с приоритетом task > project > review - [x] Auto-approve: `task.status = "done"` в auto mode - [x] Permission retry: 1 попытка с `allow_write=True` - [x] Хуки: `task_auto_approved`, `pipeline_completed`, `task_permission_retry` - [ ] Auto-followup: вызов `generate_followups()` из `run_pipeline()` в auto mode (Gap 1) - [ ] Auto-resolution `pending_actions` в auto mode (Gap 2) - [ ] API endpoints для управления `execution_mode` - [ ] Frontend UI для Auto/Review переключателя