* 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>
6.9 KiB
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 bashpython3 .../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-leveldoom_looptest_no_top_level_doom_loop_raw— raw text check: нетdoom_loop:на indent 0test_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и добавить его в opencodeAgentV2.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.