- 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>
233 lines
12 KiB
Markdown
233 lines
12 KiB
Markdown
# 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 переключателя
|