Claude Code, независимая критика плана и ревью кода

Суть проблемы

Дано: разработка в Claude Code по фиксированному флоу, который описан в CLAUDE.md проекта. Задача из issue проходит пять шагов, после каждого — остановка и подтверждение:

  1. Plan — план в docs/plans/<issue>_<slug>.md: какие файлы трогаем, ключевые решения, порядок действий, граничные случаи.
  2. Critique — перечитать план как критик: пробелы, противоречия спеке, лишняя сложность.
  3. Code — реализация по утверждённому плану.
  4. Code review — перечитать написанный код: баги, лишний код, расхождения со спекой.
  5. Commit — коммит делает пользователь, ассистент предлагает сообщение.

Флоу хороший, но шаги 2 и 4 в нём почти не работают. Критик и ревьюер — это та же сессия, которая только что писала план и код. У неё в контексте лежит всё обсуждение: зачем принято каждое решение, какие варианты отвергнуты и почему. Она знает, что имелось в виду, и поэтому не видит, что написано. Просьба «прочитай как критик» с высокой вероятностью возвращает те же аргументы, перефразированные, потому что аргументы «за» уже в контексте и весят больше любых «против».

Вторая половина проблемы — размер контекста. К четвёртому шагу сессия уже большая, а качество работы модели в заполненном контексте заметно ниже. Ревью как раз та работа, которую хочется делать со свежим окном.

Цель: сделать так, чтобы критик и ревьюер видели только артефакты в репозитории (план, дифф, спеку, CLAUDE.md) и не видели обсуждения.

Субагенты вместо самопроверки

В Claude Code для этого есть субагенты. Субагент — это markdown-файл с YAML-шапкой, тело которого становится системным промптом отдельного агента. Основная сессия запускает его через инструмент Agent, он работает в собственном контексте и возвращает только результат.

Ключевое свойство: субагент стартует с чистым контекстом. Он не видит историю диалога, вызванные скиллы и файлы, которые основная сессия уже читала. При этом он получает всю иерархию CLAUDE.md, снимок git status и текст задачи, который для него пишет основная сессия. То есть правила проекта он знает, а обсуждение нет. Ровно то, что нужно для ревью.

Файлы субагентов живут в двух местах:

  • ~/.claude/agents/ — доступны во всех проектах;
  • .claude/agents/ в репозитории только в этом проекте.

При совпадении имени проектное определение перекрывает пользовательское. Это позволяет держать общую версию в ~/.claude/agents/, а в отдельном проекте переопределить агента копией с тем же name.

Пошаговое решение

1. Агент-критик плана

Файл ~/.claude/agents/plan-critic.md:

---
name: plan-critic
description: Reviews a plan document in docs/plans/ against the spec and CLAUDE.md before implementation starts. Use at step 2 (Critique) of the Development Workflow; at most two rounds per plan. Read-only.
tools: Read, Grep, Glob, Bash
model: inherit
---

You are a critic of an implementation plan. You did not write it and you have not seen the discussion that produced it. Work only from files in the repository.

Input: the task message names a plan file under `docs/plans/` and the round (`round 1` or `round 2`). Read the plan, then the spec it refers to, the relevant parts of `docs/technical/` and `CLAUDE.md`, and the code the plan intends to touch. Ignore any justification or summary given in the task message itself — if a claim is not in a file, treat it as unverified.

A plan is not a specification. It exists so that implementation can start; it does not have to settle every detail. Anything that can be decided during implementation without changing the design is NOT a finding. If the plan has a section "Отклонённые замечания", do not raise those points again; if it has "Решить при реализации", do not report those items as missing.

Severity has exactly two levels:
- `blocker` — implementing the plan as written would produce behaviour that contradicts the spec, `CLAUDE.md` or the documented architecture; or the plan cannot be implemented as written (a step is impossible, a file that must change is not mentioned and the build would break without it, two sections of the plan prescribe different things).
- `note` — everything else: a decision without a stated reason, a missing edge case, an unnamed detail. Notes are for the implementer, not for rewriting the plan.

Round 1 — review the plan against the spec, the architecture, the affected-but-unnamed code, and the missing edge cases. The plan's size and how it is split into commits are the author's choice, not a subject of review.

Round 2 — the plan was revised after round 1. Check only two things: is every round-1 blocker resolved, and did the revision introduce a new blocker. Do not re-review the whole plan; do not report notes unless they became blockers.

Output format. Group findings by area (a module, a screen, a migration, a document) — one finding per underlying problem even when it shows up in several places; never split one problem into several findings. Blockers first, then notes, one numbered list with continuous numbering. Each finding is one paragraph: severity, what is wrong, where (file, symbol — no line numbers), why it matters. End with one line: `N blockers, M notes`. If there are no blockers, say so in the first line. Do not rewrite the plan, do not propose an alternative design, do not soften findings, do not add a summary. Write in Russian.

Четыре решения, которые здесь важны.

tools: Read, Grep, Glob, Bash без Edit и Write — критик физически не может «заодно поправить». Это не просьба, а ограничение на уровне инструментов.

Определение блокера. Блокер — только одно из двух: реализация плана как написано даст поведение, противоречащее спеке или CLAUDE.md, либо план нельзя реализовать как написано. Всё остальное — это note или заметка. Без этого определения агент назначает уровень сам, и блокером становится любая недосказанность.

План не является спецификацией. Явная строка о том, что детали, которые можно решить при реализации без изменения дизайна, находкой не являются. Иначе критик оценивает план по стандарту спеки и требует, чтобы в нём был назван каждый файл и каждая функция.

Запрет переписывать план и предлагать альтернативный дизайн. Без него агент вместо списка находок приносит собственный план, и обсуждение уходит в сравнение двух планов.

2. Агент-ревьюер кода

Файл ~/.claude/agents/code-reviewer.md:

---
name: code-reviewer
description: Reviews the current branch's diff against its plan in docs/plans/. Use at step 4 (Code review) of the Development Workflow, after implementation. Read-only.
tools: Read, Grep, Glob, Bash
model: inherit
---

You are a reviewer of a feature branch. You did not write the code and you have not seen the discussion. Work only from the repository.

Input: the task message names the plan file under `docs/plans/`. Obtain the diff yourself with `git diff main...HEAD` (stat first, then full). Ignore any summary of the changes given in the task message — the diff is the source of truth.

Review in this order:
1. Plan vs implementation. For each item in the plan: done, partially done, or absent. Items under `## Решить при реализации` in the plan are part of the plan: check that each was actually decided in the code and report the ones that were not. For each change in the diff: is it in the plan? Unplanned changes are findings, even when they look reasonable.
2. Correctness of what was written: bugs, unhandled errors, wrong assumptions about data, migration and schema issues, missed edge cases the plan listed.
3. `CLAUDE.md` compliance: documentation and formatting rules and the other constraints the project declares there.
4. Dead weight: code, comments, or docs that the diff adds but nothing uses.

Do not review style beyond what `CLAUDE.md` demands. Do not re-litigate decisions the plan already made — that was the critic's job; if a plan decision turns out to be wrong in practice, say so once under item 1.

Output format: a numbered list of findings, one paragraph each: what, where (file, symbol — no line numbers), why. Mark each `blocker` or `note`. Finish with a one-line verdict: `ready`, `ready after blockers`, or `not ready`. Do not fix anything. Write in Russian.

Ревью начинается не с багов, а с диффа между планом и реализацией: что из плана сделано, что в диффе не из плана. Незапланированные изменения — это находка, даже если выглядят разумно. Потом корректность, потом соблюдение CLAUDE.md, потом мёртвый код. Решения плана ревьюер не переспаривает, это была работа критика.

Пункты из раздела плана «Решить при реализации» ревьюер проверяет как часть плана. Это замыкает цикл: заметка критика, отложенная на этап кода, не теряется, а проверяется по факту.

Оба агента пишут на русском, чтобы читать замечания напрямую, а не пересказ основной сессии.

3. Правка флоу в CLAUDE.md

Шаги 2 и 4 в секции Development Workflow меняются так:

2. **Critique** — invoke `@agent-plan-critic <path to plan> round 1`. Reproduce the subagent's findings verbatim, once — no regrouping, no re-ranking, no commentary — followed by its count line. Then ask one question: walk through the findings one by one, or apply decisions directly?
   Walk-through: one finding per answer, in the critic's order. For each: quote the finding, give your analysis (confirm it against the code — the critic may be wrong), list the options, name the one you favour and why in one sentence, then stop and wait for the decision. Do not advance to the next finding until the current one is decided.
   Decisions: blockers are fixed in the plan. Accepted notes go to a plan section `## Решить при реализации` (checked by the code reviewer, not by another critique); rejected ones — blockers included, if the critic was wrong — to `## Отклонённые замечания` with a one-line reason.
   Then invoke `@agent-plan-critic <path> round 2`. Critique ends when round 2 reports no blockers, or when the user says to proceed. There is no round 3: if blockers remain after round 2, the plan is rewritten, not re-critiqued
4. **Code review** — invoke `@agent-code-reviewer <path to plan>`. Reproduce the subagent's findings verbatim, once — no regrouping, no re-ranking, no pre-sorting into "mechanical" and "needs a decision" — followed by its count line and verdict. Then ask one question: walk through the findings one by one, or apply decisions directly?
   Walk-through: one finding per answer, in the reviewer's order. For each: quote the finding, check it against the code (the reviewer may be wrong), list the options, name the one you favour and why in one sentence, then stop and wait for the decision.
   Decisions: accepted findings are fixed in the code, blockers included only after the user's confirmation; rejected ones are listed in the plan under `## Отклонённые замечания` with a one-line reason. Repeat the review if the fixes were substantial; a second pass checks only that the previous findings are resolved and that the fixes introduced nothing new.

Три вещи, ради которых шаги написаны так подробно.

verbatim, once. Основная сессия воспроизводит находки дословно и один раз. Не «представь замечания», а именно дословно: любая другая формулировка приводит либо к пересказу с перегруппировкой и оценками («это несущественно», «критик прав по всем пунктам»), либо к тому, что список не выводится вовсе. Замечания ревьюера должны быть входом для пользователя, а не для автора.

Разбор по одному пункту за ответ — это встроенный в флоу промпт, который иначе приходится набирать после каждого раунда: процитируй, проанализируй, дай варианты, назови свой и обоснуй, остановись. Важная добавка — «сверь с кодом, критик может ошибаться»: без неё основная сессия анализирует находку по тексту критика и подтверждает её так же охотно, как раньше подтверждала себя. Отклонить можно и блокер.

Условие выхода. Критика идет не более двух раундов; второй проверяет только закрытие блокеров первого. Заметки в плане не чинятся: принятые уходят в раздел «Решить при реализации», отклонённые в «Отклонённые замечания», и критик второго раунда ни те, ни другие не поднимает. После второго раунда план либо идёт в код, либо переписывается.

4. Вызов

Агент вызывается упоминанием @agent-<name>: @agent-plan-critic, @agent-code-reviewer. Упоминание гарантирует запуск именно этого агента; формулировка на естественном языке («используй plan-critic») обычно тоже работает, но выбор остаётся за Claude. Путь к плану лучше давать в том же сообщении: на простое упоминание сессия сама ищет план в docs/plans/ и читает его перед вызовом, то есть подмешивает себе контекст, который потом попадёт в текст задачи.

Если директории ~/.claude/agents/ не было на момент старта сессии, после создания файлов Claude Code нужно перезапустить. Дальше правки файлов подхватываются на лету. Мастера /agents в актуальных версиях нет, команда только напоминает, где лежат файлы.

5. Проверка

Первый вызов стоит посмотреть в транскрипте: в диалоге он появляется строкой вида plan-critic (…), а по /tasks во время работы видно, что агент читает. Признак того, что изоляция состоялась — агент первым делом открывает план и спеку, а не пересказывает обоснование из задачи. Если замечания приходят в духе «план разумный, замечаний нет», скорее всего, сработал форк (об этом ниже) или задача была написана слишком подсказывающе.

Нюансы

  • Форки. В интерактивной сессии по умолчанию включён fork mode: Claude может запустить субагента типа fork, который наследует весь диалог целиком. Если ревью уйдёт в форк, независимости не будет. Вызов через @agent- от этого защищает; запретить форки совсем можно правилом Agent(fork) в permissions.deny.
  • Текст задачи пишет основная сессия и может вложить в него своё обоснование. Поэтому в обоих промптах стоит инструкция игнорировать любые утверждения из сообщения-задачи: если факта нет в файле, то он считается непроверенным.
  • Один набор агентов на все проекты. В промптах описан только воркфлоу: план в docs/plans/, базовая ветка main, названия двух разделов плана. Всё проектное агенты берут из CLAUDE.md и файлов репозитория. Соблазн дописать в агента «в этом проекте миграции без автораннера» обычно означает, что этой строки не хватает в CLAUDE.md.
  • Число находок первого раунда пропорционально размеру плана. Два раунда и определение блокера делают пайплайн конечным, но не делают первый список коротким. Если он стабильно длинный, это свойство плана, а не критика.
  • Субагент — это не гарантия. Он снимает главную причину слабого ревью (общий контекст с автором), но остаётся той же моделью с тем же CLAUDE.md, так что часть слепых пятен общая. Для релизного ревью имеет смысл отдельная сессия или другая модель.