opencode-config/docs/decisions/020-pr-49-doom-loop-frontmatter.md
Sergey 2473360530
fix(agents): remove invalid doom_loop frontmatter field + simplify reviewer setup (#49)
* fix(agents): remove invalid top-level doom_loop field from frontmatter

* fix(reviewer): simplify setup step 5 and add investigation budget

* test(agents): add frontmatter validation tests

* docs(handoff): add handoff and ADR-020 for doom_loop frontmatter fix

* docs(handoff): set PR number

* docs: update project map

---------

Co-authored-by: opencode-agent <agent@slaid098.dev>
2026-07-24 19:24:36 +03:00

71 lines
No EOL
6.9 KiB
Markdown
Raw Permalink 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-020 (PR #49): Remove invalid top-level doom_loop frontmatter + reviewer doom-loop guard
## Статус
Accepted (2026-07-24)
## Контекст
При review PR #46 (reviewer ревьюил свой собственный .md файл) reviewer agent ушёл в doom loop — 75 повторений `git show f06c9f2 | grep ADR-019` за 6 минут, отменён пользователем на step 88. Investigation выявило 2 root causes:
### Root cause 1: top-level `doom_loop: deny` в agent frontmatter
Три agent-файла (`reviewer.md`, `docs-reviewer.md`, `memory-syncer.md`) содержали `doom_loop: deny` КАК top-level frontmatter поле (строка 6, indent 0, ВНЕ блока `permission:`).
Проблема: `doom_loop` НЕ входит в схему `AgentV2.Info` opencode (`packages/opencode/src/agent/agent.ts`). Unknown top-level поля форвардятся провайдеру как model parameters (passthrough). На строгих провайдерах (umans-ai-coding-plan, anthropic) это вызывает `AI_APICallError: Extra inputs are not permitted`. Подтверждено логом `opencode.log:91717`.
Валидное место для `doom_loop` — ВНУТРИ блока `permission:` (отступ 2 пробела). Там поле распознаётся opencode как permission rule (guard против doom loops на permission layer). Top-level копия — ошибочное дублирование (вероятно при миграции `config/``.opencode/` в PR#23).
### Root cause 2: self-referential ADR-019 trigger в reviewer.md Setup шаг 5
Setup шаг 5 в reviewer.md содержал inline-ссылку на ADR-019 и детальное объяснение запрета `gh pr checks` (403) и `python3 pipeline-status.py` (denied). При review своего собственного .md файла модель зациклилась на верификации этой ссылки: читает reviewer.md → видит "ADR-019" → идёт проверять существование ADR-019 (`git show f06c9f2 | grep ADR-019`) → повторяет 75 раз.
Это "крючок" (self-referential trigger) — промпт-ссылка на документ, который агент пытается верифицировать, вместо того чтобы выполнять review.
## Решение
### 1. Удалить top-level `doom_loop: deny` из frontmatter (3 файла)
Удалена строка `doom_loop: deny` на indent 0 из `reviewer.md:6`, `docs-reviewer.md:6`, `memory-syncer.md:6`.
`doom_loop: deny` ВНУТРИ блока `permission:` (indent 2, строки 8/8/8) — ОСТАВЛЕН (валидный permission guard). `steps: 100`НЕ ТРОНУТ.
### 2. Упростить Setup шаг 5 в reviewer.md
Inline-ссылка на ADR-019 и детальное объяснение запрета заменены на лаконичную инструкцию:
- `pipeline_status({pr_number: <PR_NUMBER>})` tool (primary)
- `gh run list --branch <headRefName> --limit 3` (fallback)
- "Do NOT use `gh pr checks` (403) or bash `python3 .../pipeline-status.py` (denied)" (краткий запрет без ADR-ссылки)
Запреты функционально обеспечены permission rules (frontmatter) + DANGEROUS_PATTERNS (check-permissions.py), inline-ссылка не была функциональным guard — только self-referential hook.
### 3. Investigation Budget секция
После Setup секции добавлена `## Investigation Budget`:
- Максимум ~15 steps для investigation (Setup + checklist)
- После 15 steps — обязательный `post_review`, даже при неполном review
- "An incomplete review with verdict NEEDS_DISCUSSION is better than an infinite investigation"
- "Do NOT repeatedly verify references in agent .md files — read once, assess, move on"
Structural guard против doom loops: явный step limit даёт модели сигнал остановиться, вместо бесконечной верификации.
### 4. Тесты (`tests/test_agent_frontmatter.py`)
6 тестов (pyyaml не в зависимостях — ручной парсинг frontmatter по паттерну `check-permissions.py`):
- `test_no_top_level_doom_loop` — parsed frontmatter не содержит top-level `doom_loop`
- `test_no_top_level_doom_loop_raw` — raw text check: нет `doom_loop:` на indent 0
- `test_permission_doom_loop_present``permission.doom_loop: deny` присутствует во всех 3 файлах
- `test_steps_100_present``steps: 100` присутствует во всех 3 файлах
- `test_agent_files_exist` — все 3 файла существуют
- `test_frontmatter_parseable` — frontmatter корректно парсится
## Альтернативы
- **Оставить top-level `doom_loop` и добавить его в opencode `AgentV2.Info` схему** — отклонено: opencode — upstream проект, модификация его schema вне scope этого репо. Правильное решение — убрать невалидное поле, использовать валидное место (`permission:`).
- **Удалить `permission.doom_loop` тоже (раз top-level невалиден)** — отклонено: `permission.doom_loop` — валидное и полезное поле (permission guard против doom loops на permission layer). Top-level копия — ошибка, permission-вложенное — намеренный guard. Удалять guard нельзя.
- **Убрать ADR-019 ссылку, но оставить детальное объяснение запрета** — отклонено: детальное объяснение тоже self-referential hook (модель объясняет себе, почему нельзя, вместо того чтобы не делать). Лаконичное "Do NOT" + permission rules — достаточно.
- **Investigation Budget как hard guard в type system (tool timeout)** — отклонено: opencode tools не имеют per-agent step budget (только global `steps: 100` в frontmatter). Hard guard = steps: 100 (модель упрётся в лимит). Investigation Budget — soft guard, ранний сигнал "остановись до global лимита". Лучше soft + hard, чем только hard.
- **Параметризовать ~15 в конфиге (env var / frontmatter)** — отклонено: over-engineering для текущей задачи. ~15 — эвристика из эмпирики doom loop (88 steps = слишком много, 15 = достаточно для Setup + checklist). Если понадобится тюнинг — отдельный PR.