* feat(tools): add post-review and post-docs-review tools * refactor(agents): use post-review/post-docs-review tools in reviewer and docs-reviewer * feat(permissions): add post_review and post_docs_review to agent.tools map * test(tools): add tests for post-review and post-docs-review tools * docs(handoff): add handoff ADR and project-map for review posting tools * docs(handoff): set PR number * docs: fix PR#45→PR#46 refs in project map --------- Co-authored-by: opencode-agent <agent@slaid098.dev>
7.8 KiB
| pr_number | title |
|---|---|
| 46 | Deterministic review posting tools (post-review, post-docs-review) |
PR: Deterministic review posting tools (post-review, post-docs-review)
Что сделано
.opencode/tools/post-review.ts— TS tool wrapper (3 args:pr_number,verdictenum,body). Генерирует comment## Code Review Summary\n\n${body}\n\n### Verdict: ${verdict}. Вызываетgh pr comment N --body <comment> --repo slaid098/opencode-configчерез spawnSync. Verdict enum:APPROVE,REQUEST_CHANGES,NEEDS_DISCUSSION. Паттернmerge-pr.ts(spawnSync,cwd: context.worktree)..opencode/tools/post-docs-review.ts— TS tool wrapper (3 args:pr_number,verdictenum,body). Генерирует comment## Docs Review Summary\n\n${body}\n\n### Verdict: ${verdict}. Вызываетgh pr comment N --body <comment> --repo slaid098/opencode-configчерез spawnSync. Verdict enum:APPROVE,FIXED,NO_CHANGES..opencode/agents/reviewer.md— секция "Output Format" переписана:gh pr comment→post_review({ pr_number, verdict, body }). Tool auto-generates heading + verdict line, body — содержимое между ними. 3 сценария (APPROVE/REQUEST_CHANGES/NEEDS_DISCUSSION). Frontmatter description + Rules 7/8 обновлены..opencode/agents/docs-reviewer.md— секция "PR Comment (mandatory)" переписана:gh pr comment→post_docs_review({ pr_number, verdict, body }). Frontmatter description обновлён..opencode/opencode.json—agent.toolsmap расширен:post_review+post_docs_reviewдобавлены для 4 агентов (general/reviewer/docs-reviewer/memory-syncer). reviewer: post_review=true, post_docs_review=false; docs-reviewer: post_review=false, post_docs_review=true; general+memory-syncer: оба false.tests/_ts_loader.mjs— zodShim расширен методомenum: () => chain()(дляtool.schema.enum(VERDICTS)).stripTsрасширен: stripas constassertions + striptype <Name> = ...type alias declarations (TS-only синтаксис, используется в новых tools).tests/test_post_review_tool.ts— 7 TS тестов (документационные): valid APPROVE/REQUEST_CHANGES/NEEDS_DISCUSSION, invalid verdict, heading, verdict line, spawnSync args.tests/test_post_docs_review_tool.ts— 7 TS тестов: valid APPROVE/FIXED/NO_CHANGES, invalid verdict, heading, verdict line, spawnSync args.tests/test_post_review_tool.py— 12 Python тестов через_ts_loader.mjs(exec_stub_json): load, 3 valid verdicts, invalid verdict (permissive — zod validates, not execute), heading, 3 verdict lines, spawnSync args, cwd propagation, gh failure.tests/test_post_docs_review_tool.py— 12 Python тестов: load, 3 valid verdicts, invalid verdict, heading, 3 verdict lines, spawnSync args, cwd propagation, gh failure.docs/project-map/README.md— обновлён: новые tools (post-review.ts, post-docs-review.ts), новые тесты, обновлённые описания agents (reviewer/docs-reviewer используют post_review/post_docs_review tools).- ADR-019 + этот handoff
Почему
Reviewer и docs-reviewer постят комментарии через raw gh pr comment с ручным форматированием heading + verdict. pipeline-status.py парсит эти комментарии regex'ами (REVIEW_VERDICT_RE = re.compile(r"## Code Review Summary.*?###\s*Verdict:\s*(\w+)"), DOCS_REVIEW_RE = re.compile(r"Docs Review")). Риски:
- Reviewer случайно использует wrong heading → pipeline NOT_DONE (блокировка)
- Verdict опечатан (например
APROVEвместоAPPROVE) → regex не матчит → NOT_DONE - Нет mutual exclusion между review и docs-review комментариями
DOCS_REVIEW_RE— substring match, теоретический false positive если любой комментарий упомянет "Docs Review"
Решение: детерминированные TS tools, которые гарантируют heading + verdict формат через type system (zod enum). Tool принимает body БЕЗ heading — heading auto-generated. Это устраняет root cause ошибок форматирования: агент не может опечататься в heading или verdict, т.к. они генерируются кодом, не промптом.
Паттерн merge-pr.ts (PR#30, ADR-010): thin TS wrapper → spawnSync → context.worktree как cwd. Tools auto-discovered через @opencode-ai/plugin. agent.tools map в opencode.json контролирует role-based access (reviewer → post_review, docs-reviewer → post_docs_review, general+memory-syncer → none).
Pending
- Skills/AGENTS.md правила форматов reviews теперь дублируются (agents текст + tools код). Future PR может заменить agents rules на "use post_review/post_docs_review tools" references.
gh pr comment*permission в reviewer.md/docs-reviewer.md frontmatter оставлен — tools вызывают spawnSync напрямую (не через bash permission layer). Если захотеть запретить rawgh pr comment(принудить к tool) — добавить deny правило. Сейчас оба пути работают.- pipeline-status.py regex'ы НЕ изменены (tools совместимы с существующим парсингом — генерируют ровно тот формат, который regex ожидает).
Watch out
_ts_loader.mjszodShim НЕ валидирует enum (chainable builder без checks) — enum validation происходит в opencode runtime (zod), НЕ внутри execute(). Тестtest_invalid_verdict_not_validated_by_executeдокументирует: execute() permissive, guard в zod schema. Это intentional — не дублировать валидацию в execute()._ts_loader.mjsstripTsрасширен дляas constиtype <Name> = ...— TS-only синтаксис, которого не было в предыдущих tools (commit/create-pr/create-issue/merge-pr не используют type aliases). Если будущие tools добавят другие TS-only конструкции (interface, generic, etc.) — stripTs нужно будет расширить дальше.post-review.ts/post-docs-review.tsхардкодят--repo slaid098/opencode-config(какmerge-pr.tsхардкодил--squash --delete-branch). Для других репо tools нужно параметризовать или создать копии. Альтернатива (черезgit remote) — out of scope этого PR (см. ADR-007 для паттерна параметризации).gh pr comment*в allow-list reviewer.md/docs-reviewer.md остался — tools используют spawnSync (не bash permission layer), так что rawgh pr commentвсё ещё доступен агенту. Это intentional для обратной совместимости (fallback). Запретить raw можно отдельным PR если захотеть strict tool-only.- ADR number = sequential (019), НЕ PR number. Эволюция известного паттерна (PR#26 docs-reviewer typo — записал PR number как ADR number).
- TS-тесты (
test_*.ts) — документационные, CI гоняет Python-версии через_ts_loader.mjs(bun нет на runner). - 347 тестов всего (323 существующих + 24 новых), все green. ruff check passes.