kin/tasks/adr-automode.md

234 lines
12 KiB
Markdown
Raw Permalink Normal View History

# 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()`** (строки 519536)
```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()`** (строки 453475)
```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 переключателя