feat(agents): increase step limit to 150 (#69)
* feat(agents): increase step limit to 150 * test(agents): update step limit assertions to 150 * docs(adr): add ADR-029 agent step limit 100 to 150 * docs(project-map): update tree with PR#68 changes * docs(handoff): add PR 69 handoff for agent steps 150 * docs(project-map): fix PR tags to PR#69 (actual PR number) --------- Co-authored-by: opencode-agent <agent@opencode.local>
This commit is contained in:
parent
f06d36140b
commit
063044450f
9 changed files with 73 additions and 16 deletions
|
|
@ -2,7 +2,7 @@
|
||||||
description: Reviews and updates project map documentation before code review. Auto-commits updates to PR branch. Posts verdict via post_docs_review tool (deterministic heading for pipeline-status.py).
|
description: Reviews and updates project map documentation before code review. Auto-commits updates to PR branch. Posts verdict via post_docs_review tool (deterministic heading for pipeline-status.py).
|
||||||
mode: subagent
|
mode: subagent
|
||||||
temperature: 0.1
|
temperature: 0.1
|
||||||
steps: 100
|
steps: 150
|
||||||
permission:
|
permission:
|
||||||
edit: allow
|
edit: allow
|
||||||
doom_loop: deny
|
doom_loop: deny
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
description: Distills durable knowledge from merged PR handoffs into global memory. Read-only on repo, write-only on memory.
|
description: Distills durable knowledge from merged PR handoffs into global memory. Read-only on repo, write-only on memory.
|
||||||
mode: subagent
|
mode: subagent
|
||||||
temperature: 0.1
|
temperature: 0.1
|
||||||
steps: 100
|
steps: 150
|
||||||
permission:
|
permission:
|
||||||
edit: allow
|
edit: allow
|
||||||
doom_loop: deny
|
doom_loop: deny
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
description: Global code reviewer. Reviews PRs against project skills and universal code standards. Invoke via @reviewer. Uses post_review tool to approve or request changes (deterministic heading format for pipeline-status.py). Does NOT merge — merge is done by main agent via run-pipeline.
|
description: Global code reviewer. Reviews PRs against project skills and universal code standards. Invoke via @reviewer. Uses post_review tool to approve or request changes (deterministic heading format for pipeline-status.py). Does NOT merge — merge is done by main agent via run-pipeline.
|
||||||
mode: subagent
|
mode: subagent
|
||||||
temperature: 0.1
|
temperature: 0.1
|
||||||
steps: 100
|
steps: 150
|
||||||
permission:
|
permission:
|
||||||
edit: deny
|
edit: deny
|
||||||
doom_loop: deny
|
doom_loop: deny
|
||||||
|
|
|
||||||
|
|
@ -316,7 +316,7 @@
|
||||||
},
|
},
|
||||||
"agent": {
|
"agent": {
|
||||||
"general": {
|
"general": {
|
||||||
"steps": 100,
|
"steps": 150,
|
||||||
"tools": {
|
"tools": {
|
||||||
"commit": true,
|
"commit": true,
|
||||||
"create_pr": true,
|
"create_pr": true,
|
||||||
|
|
|
||||||
28
docs/decisions/029-pr-69-agent-steps-150.md
Normal file
28
docs/decisions/029-pr-69-agent-steps-150.md
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
# ADR-029: Agent step limit 100 → 150
|
||||||
|
|
||||||
|
**Статус**: Accepted (2026-07-25, PR 69)
|
||||||
|
|
||||||
|
## Контекст
|
||||||
|
|
||||||
|
На PR#67 reviewer subagent упёрся в `MAXIMUM STEPS REACHED` — не успел вызвать `post-review` (оркестратор делал 1 retry вручную). Сообщение хардкожено в `opencode.exe` (бинарник от 2026-07-24): при `iteration_count >= agent.steps` вызывается `failUnsettledTools("Tools are disabled after the maximum agent steps")` → tools disabled. Это **hard stop**, не soft hint.
|
||||||
|
|
||||||
|
Memory `technical/opencode-log-subagent-stumbles-analysis.md` (2026-07-20) утверждала «`steps:` НЕ хард-лимит — агенты routinely превышают. Это hint, не enforcement». **Устарело**: бинарник 2026-07-24 уже hard enforcement. Подтверждено на PR#67.
|
||||||
|
|
||||||
|
ADR-020 (PR#49) унифицировал лимит до 100 для всех 4 настроенных агентов (reviewer, docs-reviewer, memory-syncer, general) как guard против doom loops. Исторически было 40/25/15/100 — но это были soft hints, превышались routinely (general до 216, reviewer до 80, docs-reviewer до 93). Сейчас 100 — hard stop, и 100 оказалось тесно для reviewer на PR с 6+ файлами diff.
|
||||||
|
|
||||||
|
Текущие значения: `reviewer.md:5`, `docs-reviewer.md:5`, `memory-syncer.md:5` — frontmatter `steps: 100`; `opencode.json:319` — `agent.general.steps: 100`. Тест `test_agent_frontmatter.py:154` pin'ит `fm["steps"] == "100"` для всех 3 `.md` (uniformity). Тест `test_permissions.py:119` pin'ит `general.steps == 100` (int, JSON).
|
||||||
|
|
||||||
|
## Решение
|
||||||
|
|
||||||
|
Повысить лимит шагов subagent-ов со 100 до 150 для всех 4 настроенных агентов (reviewer, docs-reviewer, memory-syncer, general). Uniformity сохранена (ADR-020 principle) — memory-syncer безвредно получит 150 (редко доходит до лимита, задача простая). Trade-off: +50% budget → +50% doom-loop risk, но guard сохранён. Без архитектурного рефакторинга (вынос explore-фазы reviewer'а в отдельный `explore` subagent) — отложено в отдельный PR если 150 снова станет тесно.
|
||||||
|
|
||||||
|
Soft Investigation Budget в `reviewer.md:103-108` (~15 шагов) остаётся рекомендацией, hard limit 150 — guard.
|
||||||
|
|
||||||
|
Тесты обновлены: `test_steps_100_present` → `test_steps_150_present`, assertion `fm["steps"] == "150"`, `test_permissions.py` assertion `general.steps == 150`.
|
||||||
|
|
||||||
|
## Альтернативы
|
||||||
|
|
||||||
|
- **200/300** — отклонено: избыточный doom-loop риск, +100%/+200% budget без явной необходимости.
|
||||||
|
- **∞ (убрать лимит)** — отклонено: нет guard'а вообще, doom loop может длиться часами (контр-показания из memory `opencode-log-subagent-stumbles-analysis.md`).
|
||||||
|
- **Рефакторинг (вынос explore-фазы reviewer'а в отдельный `explore` subagent с ∞ steps)** — отклонено в этом PR: scope creep. Отдельный PR если 150 снова станет тесно.
|
||||||
|
- **Per-agent разные значения (нарушить uniformity)** — отклонено: ADR-020 principle — uniformity упрощает тестирование и reasoning. memory-syncer мог бы остаться на 100, но это усложнило бы тест (`per-agent expected` map) без выгоды.
|
||||||
29
docs/handoff/pr-69-agent-steps-150.md
Normal file
29
docs/handoff/pr-69-agent-steps-150.md
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
---
|
||||||
|
pr: 69
|
||||||
|
title: feat(agents): increase step limit to 150
|
||||||
|
---
|
||||||
|
|
||||||
|
## Что сделано
|
||||||
|
Повышен лимит шагов subagent-ов со 100 до 150 для всех 4 настроенных агентов (reviewer, docs-reviewer, memory-syncer, general). Обновлены 4 конфига + 2 тест-файла (5 правок в test_agent_frontmatter.py + 1 в test_permissions.py). Создан ADR-029 (docs/decisions/029-pr-69-agent-steps-150.md).
|
||||||
|
|
||||||
|
Конкретно:
|
||||||
|
- .opencode/agents/reviewer.md:5, docs-reviewer.md:5, memory-syncer.md:5 — frontmatter `steps: 100` → `steps: 150`
|
||||||
|
- .opencode/opencode.json:319 — `agent.general.steps: 100` → `150`
|
||||||
|
- tests/test_agent_frontmatter.py — module docstring, section comment, rename `test_steps_100_present` → `test_steps_150_present`, docstring, assertion `fm["steps"] == "150"` (string)
|
||||||
|
- tests/test_permissions.py:119 — assertion `general.steps == 150` (int, JSON)
|
||||||
|
- docs/project-map/README.md — теги PR#68 на 5 строках (3 agent .md, opencode.json, 2 test-файла)
|
||||||
|
|
||||||
|
## Почему
|
||||||
|
PR#67 reviewer упёрся в `MAXIMUM STEPS REACHED` (hard stop с бинарника 2026-07-24, реализует `failUnsettledTools` при `iteration_count >= agent.steps` → tools disabled), не успел вызвать `post-review` — оркестратор делал 1 retry вручную. 100 оказалось тесно для reviewer на PR с 6+ файлами diff.
|
||||||
|
|
||||||
|
Memory `technical/opencode-log-subagent-stumbles-analysis.md` (2026-07-20) утверждала «`steps:` НЕ хард-лимит — hint, не enforcement» — устарело: бинарник 2026-07-24 уже hard stop. Подтверждено на PR#67. ADR-029 фиксирует обновление и trade-off (+50% budget → +50% doom-loop risk, но guard сохранён).
|
||||||
|
|
||||||
|
## Pending
|
||||||
|
- memory update `opencode-log-subagent-stumbles-analysis.md` (soft hint → hard stop) — на фазе MEMORY через memory-syncer subagent
|
||||||
|
- Рефакторинг (вынос explore-фазы reviewer'а в отдельный `explore` subagent с ∞ steps) — отложено в отдельный PR если 150 снова станет тесно
|
||||||
|
|
||||||
|
## Watch out
|
||||||
|
- global copy `/root/.config/opencode/agents/*.md` — bind-mount, лимит 150 вступит в силу после `git pull` + container restart. В текущем контейнере лимит остаётся 100 до рестарта.
|
||||||
|
- scaffold-handoff.sh нумерация ADR по `ls docs/decisions | wc -l + 1` — если ADR создан вручную ДО scaffold, scaffold найдёт существующий файл по `*-pr-<N>-<slug>.md` паттерну и не создаст дубликат (как в этом PR). Если создать вручную с placeholder в имени (029-pr-PR-NUMBER-...), нужно переименовать ДО запуска scaffold, иначе scaffold не найдёт и создаст дубликат 030.
|
||||||
|
- `test_permissions.py:119` проверяет int `150` (JSON, json parser → int), `test_agent_frontmatter.py:154` — строку `"150"` (frontmatter parser возвращает строки, не int). Не путать типы.
|
||||||
|
- package-lock.json untracked в репо — не относится к этому PR, не коммитить.
|
||||||
|
|
@ -16,9 +16,9 @@ opencode-config/
|
||||||
│ └── dependabot.yml # pip + github-actions ecosystem updates
|
│ └── dependabot.yml # pip + github-actions ecosystem updates
|
||||||
├── .opencode/ # Project-local opencode config (auto-discovery, zero env var) — PR#23
|
├── .opencode/ # Project-local opencode config (auto-discovery, zero env var) — PR#23
|
||||||
│ ├── agents/
|
│ ├── agents/
|
||||||
│ │ ├── docs-reviewer.md # Docs validation subagent (project map + handoff + ADR, uses `commit`+`post_docs_review` tools) — PR#40, PR#46
|
│ │ ├── docs-reviewer.md # Docs validation subagent (project map + handoff + ADR, uses `commit`+`post_docs_review` tools) — PR#40, PR#46, PR#69
|
||||||
│ │ ├── memory-syncer.md # Distills gotchas from handoffs into opencode-memory
|
│ │ ├── memory-syncer.md # Distills gotchas from handoffs into opencode-memory — PR#69
|
||||||
│ │ └── reviewer.md # Code review subagent (verdict via `post_review` tool: APPROVE|REQUEST_CHANGES|NEEDS_DISCUSSION) — PR#46
|
│ │ └── reviewer.md # Code review subagent (verdict via `post_review` tool: APPROVE|REQUEST_CHANGES|NEEDS_DISCUSSION) — PR#46, PR#69
|
||||||
│ ├── commands/
|
│ ├── commands/
|
||||||
│ │ ├── configure-opencode.md # /configure-opencode — edit opencode.json
|
│ │ ├── configure-opencode.md # /configure-opencode — edit opencode.json
|
||||||
│ │ ├── run-pipeline.md # /run-pipeline — 7-phase PR pipeline
|
│ │ ├── run-pipeline.md # /run-pipeline — 7-phase PR pipeline
|
||||||
|
|
@ -59,7 +59,7 @@ opencode-config/
|
||||||
│ │ ├── setup-memory.sh # opencode-memory bootstrap (deterministic 6-step flow, idempotent) — PR#36
|
│ │ ├── setup-memory.sh # opencode-memory bootstrap (deterministic 6-step flow, idempotent) — PR#36
|
||||||
│ │ ├── spec-status.py # 9-phase spec oracle
|
│ │ ├── spec-status.py # 9-phase spec oracle
|
||||||
│ │ └── tunnel.sh # Cloudflare tunnel toggle bash (named mode via CLOUDFLARE_TUNNEL_TOKEN) — PR#34
|
│ │ └── tunnel.sh # Cloudflare tunnel toggle bash (named mode via CLOUDFLARE_TUNNEL_TOKEN) — PR#34
|
||||||
│ ├── opencode.json # MCP servers, providers, permissions, agents (role-based tools), plugins — PR#40
|
│ ├── opencode.json # MCP servers, providers, permissions, agents (role-based tools), plugins — PR#40, PR#69
|
||||||
│ ├── package.json # npm deps for tools/*.ts
|
│ ├── package.json # npm deps for tools/*.ts
|
||||||
│ └── .gitignore # Ignores node_modules, etc.
|
│ └── .gitignore # Ignores node_modules, etc.
|
||||||
├── docs/
|
├── docs/
|
||||||
|
|
@ -76,7 +76,7 @@ opencode-config/
|
||||||
│ └── search.py # Search
|
│ └── search.py # Search
|
||||||
├── tests/ # pytest + TS/MJS test suite — PR#17
|
├── tests/ # pytest + TS/MJS test suite — PR#17
|
||||||
│ ├── _ts_loader.mjs # TS test loader (load/exec_stub/exec_stub_json/exec_real modes; relative import inlining via inlineShared()) — PR#38, PR#65
|
│ ├── _ts_loader.mjs # TS test loader (load/exec_stub/exec_stub_json/exec_real modes; relative import inlining via inlineShared()) — PR#38, PR#65
|
||||||
│ ├── test_agent_frontmatter.py # Agent frontmatter validators (no top-level doom_loop, permission.doom_loop present, steps:100) — PR#49
|
│ ├── test_agent_frontmatter.py # Agent frontmatter validators (no top-level doom_loop, permission.doom_loop present, steps:150) — PR#49, PR#69
|
||||||
│ ├── test_check_adr_refs.py # adr-check.yml validator
|
│ ├── test_check_adr_refs.py # adr-check.yml validator
|
||||||
│ ├── test_check_permissions.py # permissions-check.yml validator
|
│ ├── test_check_permissions.py # permissions-check.yml validator
|
||||||
│ ├── test_cli.py # src/memory/cli.py
|
│ ├── test_cli.py # src/memory/cli.py
|
||||||
|
|
@ -95,7 +95,7 @@ opencode-config/
|
||||||
│ ├── test_merge_pr_tool.py # .opencode/tools/merge-pr.ts (via _ts_loader.mjs; 8 tests: base + repo explicit/omitted/invalid) — PR#65
|
│ ├── test_merge_pr_tool.py # .opencode/tools/merge-pr.ts (via _ts_loader.mjs; 8 tests: base + repo explicit/omitted/invalid) — PR#65
|
||||||
│ ├── test_merge_pr_tool.ts # TS wrapper test (mjs loader; 5 tests: base + repo) — PR#65
|
│ ├── test_merge_pr_tool.ts # TS wrapper test (mjs loader; 5 tests: base + repo) — PR#65
|
||||||
│ ├── test_observability.py # .opencode/scripts/observability.py
|
│ ├── test_observability.py # .opencode/scripts/observability.py
|
||||||
│ ├── test_permissions.py # Global deny rules + agent.<name>.tools role-based access (8 tests) — PR#40
|
│ ├── test_permissions.py # Global deny rules + agent.<name>.tools role-based access (8 tests) — PR#40, PR#69
|
||||||
│ ├── test_pipeline_status.py # .opencode/scripts/pipeline-status.py (REVIEW verdict branching)
|
│ ├── test_pipeline_status.py # .opencode/scripts/pipeline-status.py (REVIEW verdict branching)
|
||||||
│ ├── test_pipeline_status_adr.py
|
│ ├── test_pipeline_status_adr.py
|
||||||
│ ├── test_pipeline_status_ci.py
|
│ ├── test_pipeline_status_ci.py
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ Covers issue #48 acceptance criteria:
|
||||||
``AI_APICallError: Extra inputs are not permitted`` on strict providers.
|
``AI_APICallError: Extra inputs are not permitted`` on strict providers.
|
||||||
- ``doom_loop`` INSIDE the ``permission:`` block IS present in all 3 files
|
- ``doom_loop`` INSIDE the ``permission:`` block IS present in all 3 files
|
||||||
(valid — it is the real permission guard).
|
(valid — it is the real permission guard).
|
||||||
- ``steps: 100`` is present and unchanged in all 3 files.
|
- ``steps: 150`` is present and unchanged in all 3 files.
|
||||||
|
|
||||||
PyYAML is not a project dependency, so frontmatter is parsed manually by
|
PyYAML is not a project dependency, so frontmatter is parsed manually by
|
||||||
splitting on ``---`` delimiters and tracking indentation (top-level fields have
|
splitting on ``---`` delimiters and tracking indentation (top-level fields have
|
||||||
|
|
@ -143,15 +143,15 @@ def test_permission_doom_loop_present():
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# ── steps: 100 present and unchanged ─────────────────────────────────────────
|
# ── steps: 150 present and unchanged ─────────────────────────────────────────
|
||||||
|
|
||||||
|
|
||||||
def test_steps_100_present():
|
def test_steps_150_present():
|
||||||
"""``steps: 100`` is present in all 3 agent files (must not be changed)."""
|
"""``steps: 150`` is present in all 3 agent files (must not be changed)."""
|
||||||
for f in AGENT_FILES:
|
for f in AGENT_FILES:
|
||||||
fm = _parse_frontmatter(f)
|
fm = _parse_frontmatter(f)
|
||||||
assert "steps" in fm, f"{f.name}: 'steps' field missing"
|
assert "steps" in fm, f"{f.name}: 'steps' field missing"
|
||||||
assert fm["steps"] == "100", f"{f.name}: steps must be '100', got {fm['steps']!r}"
|
assert fm["steps"] == "150", f"{f.name}: steps must be '150', got {fm['steps']!r}"
|
||||||
|
|
||||||
|
|
||||||
# ── frontmatter is well-formed (parses without error) ───────────────────────
|
# ── frontmatter is well-formed (parses without error) ───────────────────────
|
||||||
|
|
|
||||||
|
|
@ -116,7 +116,7 @@ def test_general_tools():
|
||||||
assert tools["create_pr"] is True
|
assert tools["create_pr"] is True
|
||||||
assert tools["create_issue"] is True
|
assert tools["create_issue"] is True
|
||||||
assert tools["merge_pr"] is False
|
assert tools["merge_pr"] is False
|
||||||
assert _load_config()["agent"]["general"]["steps"] == 100
|
assert _load_config()["agent"]["general"]["steps"] == 150
|
||||||
|
|
||||||
|
|
||||||
# ── agent.reviewer.tools ────────────────────────────────────────────────────
|
# ── agent.reviewer.tools ────────────────────────────────────────────────────
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue