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

6.9 KiB
Raw Blame History

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_presentpermission.doom_loop: deny присутствует во всех 3 файлах
  • test_steps_100_presentsteps: 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.