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>
This commit is contained in:
parent
3cb516193b
commit
4a27bf0693
12 changed files with 2698 additions and 30 deletions
233
tasks/adr-automode.md
Normal file
233
tasks/adr-automode.md
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
# 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 переключателя
|
||||
Loading…
Add table
Add a link
Reference in a new issue