kin/tasks/adr-automode.md
Gros Frumos 4a27bf0693 feat(KIN-012): UI auto/review mode toggle, autopilot indicator, persist project mode in DB
- TaskDetail: hide Approve/Reject buttons in auto mode, show "Автопилот активен" badge
- TaskDetail: execution_mode persisted per-task via PATCH /api/tasks/{id}
- TaskDetail: loadMode reads DB value, falls back to localStorage per project
- TaskDetail: back navigation preserves status filter via ?back_status query param
- ProjectView: toggleMode now persists to DB via PATCH /api/projects/{id}
- ProjectView: loadMode reads project.execution_mode from DB first
- ProjectView: task list shows 🔓 badge for auto-mode tasks
- ProjectView: status filter synced to URL query param ?status=
- api.ts: add patchProject(), execution_mode field on Project interface
- core/db.py, core/models.py: execution_mode columns + migration for projects & tasks
- web/api.py: PATCH /api/projects/{id} and PATCH /api/tasks/{id} support execution_mode
- tests: 256 tests pass, new test_auto_mode.py with 60+ auto mode tests
- frontend: vitest config added for component tests

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 20:02:01 +02:00

233 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 переключателя