diff --git a/EXPERIMENT.md b/EXPERIMENT.md index 9569288..ff791be 100644 --- a/EXPERIMENT.md +++ b/EXPERIMENT.md @@ -1,8 +1,25 @@ # Эксперимент: как две модели ревьюят один и тот же план -Opus и GPT-5.4 получили одинаковый план. Нашли разные проблемы. -Ноль полных совпадений из 9 находок. Этот документ — полный ход -эксперимента, от замысла до выводов. +Opus и GPT-5.4 получили одинаковый план и одинаковую adversarial +установку. Нашли разные проблемы. Этот документ — полный ход +эксперимента: от наивного запуска до контролируемого сравнения. + +## Предыстория: как мы вообще сюда попали + +Эксперимент не планировался. Изначальная цель — протестировать +скилл `/claude-team-review` на реальной задаче. Я выбрал фичу +из roadmap'а (persistent reviewer memory) и начал планировать. + +По ходу работы стало интересно: а что если прогнать тот же план +через `/adversarial-review` (Codex)? Просто посмотреть, что найдёт +другая модель. Одно потянуло за другое — последовательное ревью +показало нулевой overlap, я усомнился в методологии, пересобрал +эксперимент с контролем. + +Важный контекст: у меня были сомнения в полезности самой фичи +persistent memory. Поэтому работа шла в отдельном бранче +`feat/reviewer-memory` — мержить только если фича докажет +свою ценность. ## Контекст @@ -14,6 +31,11 @@ Opus и GPT-5.4 получили одинаковый план. Нашли ра - **claude-team-review** — Claude пишет, другой Claude ревьюит (Agent Teams). Одна модельная семья, но изолированные контексты. +Оба скилла используют одинаковую adversarial stance: ревьюер +по умолчанию скептичен, каждая находка обязана ответить на 4 +вопроса (что сломается, почему уязвим, импакт, рекомендация), +нельзя комментировать стиль и спекулятивные улучшения. + Вопрос: насколько отличаются находки двух ревьюеров? Находят ли они одно и то же? Или каждый видит своё? @@ -31,7 +53,70 @@ Opus и GPT-5.4 получили одинаковый план. Нашли ра - Флаг `nomemory` для отключения - Edge cases и верификацию -## Фаза 1: последовательное ревью (ошибка методологии) +## Как создавался план + +План писался в Claude Code Plan Mode. Сначала Explore-агент изучил +структуру репо (SKILL.md, adversarial-reviewer.md, README.md), потом +Plan-агент спроектировал изменения. Я дополнил и скорректировал. + +Первая попытка сохранить план в файл — пользователь отклонил: +"Для чего ты сохраняешь в файл?" Вопрос был не праздный — для +тестирования скилла файл не обязателен, план может быть в контексте +разговора. Скилл поддерживает оба варианта. Это уже первый +полезный инсайт: при тестировании всплывают UX-вопросы, которые +не видны при написании. + +В итоге план ушёл в Plan Mode file +(`~/.claude/plans/dapper-herding-whisper.md`) — стандартный механизм +Claude Code. + +## Как работает Team Review технически + +Для контекста будущей статьи — как устроен процесс под капотом: + +1. **TeamCreate** — создаёт команду (`~/.claude/teams/review-memory-plan/`) + и связанный task list +2. **Agent spawn** — создаёт teammate типа `adversarial-reviewer` + (определение в `adversarial-reviewer.md`: model=opus, effort=high, + tools=Read/Grep/Glob/Bash/WebSearch/Context7, disallowedTools=Write/Edit) +3. **Briefing** — lead отправляет reviewer'у промпт с описанием + задачи и путём к плану. Reviewer сам читает файлы, изучает репо +4. **Findings** — reviewer отвечает в формате Summary/Findings/Verdict +5. **Fixes** — lead правит план и отправляет **тому же** reviewer'у + через SendMessage. Контекст сохраняется — reviewer помнит прошлый + раунд, не перечитывает проект +6. **Cleanup** — SendMessage с shutdown_request, затем TeamDelete + +Ключевая деталь: teammate **не перезапускается** между раундами. +Это отличает Agent Teams от простого Agent spawn — teammate живёт +между сообщениями. Мне пришлось объяснять это в разговоре, когда +возник вопрос "твой teammate после каждого ответа перезапускает +сессию?" Нет — внутри сессии контекст нативный. Persistent memory +решает другую проблему: контекст между сессиями (закрыл терминал, +открыл завтра). + +## Как работает Adversarial Review технически + +Для сравнения — как устроен процесс с Codex: + +1. **Prompt file** — lead пишет промпт в `/tmp/codex-prompt-{id}.md` + через Write tool (не bash, чтобы избежать quoting issues) +2. **codex exec** — `timeout 600 codex exec -m gpt-5.4 + -c model_reasoning_effort=high -s read-only + -o /tmp/codex-review-{id}.md - < /tmp/codex-prompt-{id}.md` + Синхронный вызов, 10 минут timeout +3. **Session ID** — извлекается из stderr (`session id: uuid`) +4. **Resume** — `codex exec resume {session_id} - < prompt` + Продолжает сессию, экономит токены. Но есть нюансы (см. статью) +5. **Cleanup** — `rm -f /tmp/codex-*-{id}.*` + +Разница в capabilities: Codex работает в read-only sandbox (может +только читать файлы и запускать git). Opus-teammate может запускать +тесты, лinter'ы, обращаться к Context7 (MCP), искать в интернете. +В этом эксперименте разница не существенна (ревьюим план, не код), +но для code review может влиять. + +## Фаза 1: последовательное ревью Сначала я запустил ревью последовательно, как обычно делаю в работе: @@ -39,9 +124,9 @@ Opus и GPT-5.4 получили одинаковый план. Нашли ра План v1 → Opus (2 раунда) → План v2 → Codex (5 раундов) → План v3 ``` -### Opus (claude-team-review): 6 находок за 1 раунд +### Opus (claude-team-review): Round 1 — 6 находок, Round 2 — approve -Opus получил оригинальный план v1 и выдал 6 находок сразу: +Opus получил оригинальный план v1 и выдал 6 находок в первом раунде: **1. [high] Merge-логика слишком сложна для prompt-based системы** @@ -87,12 +172,36 @@ Lead должен парсить свободный текст reviewer'а и з Правило "Avoid creating auxiliary files (memory files...)" запрещает memory files, но план создаёт именно memory file. -**После фиксов — Opus одобрил план на 2-м раунде.** +**Как фиксил:** + +Все 6 находок принял. Самый значимый фикс — #1 (merge-логика): +полностью убрал partial merge, заменил на full rewrite. Это не +"подправить формулировку" — это изменение архитектурного решения. +Lead больше не парсит секции и не управляет cap'ами — он просто +пишет файл целиком из ответа reviewer'а. + +Для #2 (шаблон) — добавил точный markdown-template в оба файла. +Для #3 (парсинг) — описал порядок: сначала извлекать флаги, +потом определять mode. Для #4 (gitignore) — добавил рекомендацию. +Для #5 (cap) — убрал отдельный read cap, заменил на детектор +повреждения (>500 строк = caps не сработали, регенерировать). +Для #6 — убрал "memory files" из списка запретов. + +Во втором раунде Opus проверил фиксы и одобрил план — новых +проблем не нашёл. Проверка заняла заметно меньше времени: reviewer +уже знал проект и план, просто верифицировал изменения. ### Codex (adversarial-review): 7 находок за 5 раундов -Codex получил уже улучшенный план v2 (после фиксов Opus'а) и нашёл -ещё 7 проблем. По одной-две за раунд, каждый раз копая глубже: +Codex получил уже улучшенный план v2 (после фиксов Opus'а). Это +важная деталь: Codex ревьюил не тот же вход, что Opus. Грубые +проблемы (merge-логика, отсутствие шаблона) уже были исправлены. + +Тем не менее, Codex нашёл ещё 7 проблем — по одной-две за раунд, +каждый раз копая глубже. Все 7 — severity high. Это не случайность: +Opus убрал проблемы уровня "спецификация" и "архитектура", остались +проблемы уровня "безопасность" и "корректность" — те, что Opus +не тронул. **Раунд 1 — две находки:** @@ -163,28 +272,104 @@ README line 109: `cp .claude/agents/adversarial-reviewer.md ~/.claude/agents/`. Рекомендация: убрать безусловное утверждение, рекомендовать gitignore по умолчанию, коммитить только после проверки содержимого. -**5 раундов, максимум достигнут. Последний фикс не был verify.** +5 раундов, максимум достигнут. Последний фикс не был verify. -### Что бросилось в глаза +**Как фиксил находки Codex:** + +Раунд 1: для #1 (injection) — обернул memory в `` +с явным ограждением, добавил валидацию структуры при загрузке. +Для #2 (late persistence) — добавил промежуточные checkpoints. + +Раунд 2: #3 (section name) — унифицировал `## Review log` везде. +#4 (nomemory) — добавил gate на все memory-операции, включая +checkpoints. + +Раунд 3: #5 (ложные resolved) — промежуточные checkpoints теперь +не трогают Resolved findings, только финальный Step 6b. + +Раунд 4: #6 (путь в README) — исправил путь установки в плане. +Это оказался реальный баг в текущей документации проекта. + +Раунд 5: #7 (no secrets) — убрал безусловное утверждение, сменил +рекомендацию на gitignore по умолчанию. + +Характерная разница в фиксах: для Opus я менял **архитектуру** +(переписал merge-логику). Для Codex я добавлял **защиты** +(валидации, gates, ограждения). Разный характер находок → +разный характер фиксов. + +### Стоп. Мы сравниваем не то, что думаем 13 находок суммарно. Ноль пересечений. Ни одна находка Codex -не дублировала находку Opus. +не дублировала находку Opus. Казалось бы — идеальная комплементарность. -Но это нечестное сравнение: Codex ревьюил улучшенный план. -Opus мог бы найти те же проблемы в оригинале. Или Codex мог бы -найти другие проблемы, если бы видел merge-логику до упрощения. +На этом этапе я (Дмитрий) сформулировал наблюдение: -## Фаза 2: параллельное ревью (контролируемый эксперимент) +> Опус, как ревьюер, рассуждает с позиции архитектора. Видя проблему +> больше сверху. Кодекс — больше как тщательный исполнитель, копает +> нюансы конкретного плана, в сторону уязвимостей/косяков. -Осознав проблему, я сохранил оригинальный план v1 и отправил его -Codex отдельно — на том же входе, что получил Opus. +Красивый вывод. И вот сводная таблица, которую мы составили +на этом этапе — ещё до осознания методологической проблемы: + +### Сводка последовательного ревью (Opus на v1, Codex на v2) + +| # | Opus (на v1) | Codex (на v2) | +|---|-------------|---------------| +| 1 | **Merge-логика слишком сложна** — LLM не потянет partial merge в prompt-based системе [high] | **Prompt injection** — memory verbatim в промпте, можно отравить [high] | +| 2 | **Нет шаблона обмена** — lead парсит свободный текст, хрупко [high] | **Memory пишется слишком поздно** — прерванные сессии теряют контекст [high] | +| 3 | **`nomemory` парсинг** — не описаны комбинации аргументов [medium] | **Section name mismatch** — шаблон и валидатор не согласованы [high] | +| 4 | **gitignore** — нет рекомендации коммитить или игнорить [medium] | **`nomemory` не гейтит checkpoints** — opt-out неполный [high] | +| 5 | **500-line cap** — поведение при превышении не определено [medium] | **Checkpoint пишет ложные resolved** — lead предполагает вместо reviewer'а [high] | +| 6 | **Противоречие в правилах** — "memory files" в списке запретов [low] | **README path mismatch** — установка по документации не работает [high] | +| 7 | — | **"Contains no secrets"** — unsafe claim, findings могут содержать sensitive data [high] | + +**13 находок. 0 пересечений. 6 vs 7.** + +Наглядно видно: Opus нашёл **проблемы дизайна** (сложность, +контракт, спецификация). Codex нашёл **проблемы корректности +и безопасности** (injection, data leak, broken contracts, +ложное состояние). Разные этажи одного здания. + +Но тут же возник вопрос: **а честное ли сравнение?** + +Codex ревьюил другой вход. Opus получил план v1. Codex получил +план v2 — уже без merge-логики, с точным шаблоном, с описанным +парсингом флагов. Конечно находки не пересекаются: половину +проблем уже исправили. + +Наблюдение дословно: + +> У нас эксперимент интересный, но не полный. Мы ревьюили +> последовательно. Сначала улучшили план одним агентом, затем, +> после полировки — другим. + +Может быть, Codex нашёл бы merge-логику на v1? Может быть, Opus +нашёл бы prompt injection, если бы мы не убрали другие проблемы? +Мы не знаем. Последовательное ревью хорошо для качества плана, +но бесполезно для сравнения ревьюеров. + +Нужен контролируемый эксперимент: оба ревьюера на одном и том же +входе. + +## Фаза 2: параллельное ревью (контролируемое сравнение) + +Я сохранил оригинальный план v1 и отправил его Codex отдельно. +Тот же вход, та же adversarial установка, тот же промпт-шаблон. +Единственная разница — модель. ``` - ┌→ Opus → 6 находок + ┌→ Opus → 6 находок (полный цикл: 2 раунда) План v1 ──┤ - └→ Codex → 3 находки + └→ Codex → 3 находки (один прогон, без итераций) ``` +Важная оговорка: Codex на v1 запускался одним прогоном без +итеративного цикла фиксов. Opus прошёл 2 полных раунда. Сравнение +не полностью симметричное — Codex мог бы найти больше за 5 раундов. +Но первый раунд — самый информативный: именно он показывает, +на что модель обращает внимание в первую очередь. + ### Codex на плане v1: 3 находки **1. [high] Memory может утечь в git** @@ -206,7 +391,7 @@ Codex отдельно — на том же входе, что получил Op > режет файл посреди секции. После нескольких циклов файл > деградирует. -## Сравнение: Opus vs Codex на одном и том же входе +## Сравнение: Opus vs Codex на одном входе ### Все находки рядом @@ -235,16 +420,29 @@ Codex отдельно — на том же входе, что получил Op **Только Codex:** утечка данных, monorepo path resolution. -### Характер мышления +### Характер мышления — главная таблица -| | Opus | Codex | -|---|------|-------| +Эта таблица — ключевой артефакт эксперимента. Она показывает +не просто разницу в находках, а разницу в **способе думать**: + +| Аспект | Opus | Codex | +|--------|------|-------| +| **Кто он** | Архитектор | Въедливый, тщательный исполнитель | | **Главный вопрос** | "Сможет ли система это выполнить?" | "Что сломается в реальном мире?" | -| **Роль** | Архитектор | Security/ops инженер | +| **Фокус** | Внутренняя согласованность, feasibility | Failure modes, безопасность, edge cases | | **Лучшая находка** | Merge-логика → упрощение архитектуры | Project root → monorepo сценарий | -| **Слепое пятно** | Security (не нашёл injection, data leak) | Feasibility (не сомневается что LLM справится) | +| **Слепое пятно** (в этом эксперименте) | Security (не нашёл injection, data leak) | Feasibility (не сомневается что LLM справится с merge) | | **Стиль рекомендаций** | "Сделай проще" | "Добавь защиту" | +| **Самые ценные находки** | Упрощение merge-логики (архитектурное решение) | Prompt injection, late persistence (неочевидные failure modes) | +| **Слабые находки** | Rule contradiction (косметика) | — (все medium+) | | **Темп** | 6 находок сразу, approve на Round 2 | 1-2 за раунд, копает послойно | +| **Пересечения** | 3 темы — видит как проблемы дизайна/UX | 3 темы — видит как проблемы безопасности/корректности | +| **Характер фиксов** | Меняешь архитектуру | Добавляешь защиты | + +Opus видит здание сверху: "фундамент кривой, перестрой". Codex +ходит по этажам и проверяет каждую дверь: "этот замок можно +открыть отвёрткой, тут нет пожарного выхода, а тут табличка +врёт". ## Выводы @@ -269,14 +467,14 @@ Codex думает как security/ops инженер: "что если злоу Одна и та же дыра в плане, но один ревьюер предложит добавить строчку в README, а другой — пересмотреть, где хранить файл. -### 3. Последовательное ревью скрывает реальную картину +### 3. Последовательное ревью полезно, но скрывает реальную картину В последовательном режиме (Opus → Codex) мы увидели 13 уникальных находок и решили, что overlap нулевой. Но это артефакт: Codex ревьюил улучшенный план. При параллельном запуске на одном входе — 9 находок с 30% частичным пересечением. -Для оценки моделей — параллельный запуск. Для максимального +Для сравнения моделей — параллельный запуск. Для максимального качества плана — последовательный (второй ревьюер находит то, что первый пропустил даже после фиксов). @@ -298,14 +496,17 @@ Codex думает как security/ops инженер: "что если злоу ## Числа -| Метрика | Opus | Codex (на v1) | Codex (на v2, sequential) | -|---------|------|---------------|---------------------------| +| Метрика | Opus (на v1) | Codex (на v1) | Codex (на v2, sequential) | +|---------|--------------|---------------|---------------------------| | Находок | 6 | 3 | 7 | -| Раундов до approve/max | 2 | 1 (single pass) | 5 (max reached) | +| Раундов | 2 (approve) | 1 (single pass*) | 5 (max reached) | | High severity | 2 | 2 | 7 | | Уникальных (не найдено другим) | 3 | 2 | 7 | | Частичных пересечений | 3 темы | 3 темы | 0 (другой вход) | +*Codex на v1 запускался одним прогоном для сравнения первой реакции. +Полный итеративный цикл на v1 не проводился. + ## Ограничения эксперимента - Один эксперимент на одном плане — может не обобщаться. @@ -315,8 +516,66 @@ Codex думает как security/ops инженер: "что если злоу доступом к командам. Разные возможности могут влиять на находки. - Один и тот же human (lead) фиксил для обоих — интерпретация lead'а добавляет переменную. -- Параллельное сравнение — только Round 1 Codex (без итераций). - Полный итеративный цикл на v1 не проводился. +- Параллельное сравнение асимметрично: Opus прошёл 2 раунда, + Codex — 1. Codex мог бы найти больше за полный цикл. +- Характеристики "архитектор" и "ops-инженер" — наблюдение из + одного эксперимента, не универсальное свойство моделей. + +## Наблюдения по ходу работы + +Мелочи, которые не вошли в основной текст, но могут пригодиться: + +**Opus одобряет быстрее.** 6 находок в Round 1, approve в Round 2. +Codex на v2 шёл 5 раундов и не одобрил. Возможная причина: Opus +выдаёт всё сразу (широкий взгляд), Codex копает послойно — каждый +фикс открывает новый слой проблем. Для пользователя: Opus-ревью +быстрее, Codex-ревью глубже. + +**Codex находил баги в моих фиксах.** Находки #3 и #4 (section +name mismatch, nomemory не гейтит checkpoints) — это баги, +внесённые фиксами к находкам Opus'а. Второй ревьюер ловит ошибки +первого цикла фиксов. Аргумент в пользу последовательного ревью. + +**Codex нашёл баг в существующей документации.** Находка #6 +(README path) — не про план, а про текущий README. Побочный +эффект: ревью плана обнаружило проблему в проекте, которая +существовала до начала работы. + +**Характер фиксов разный.** Для Opus — меняешь архитектуру +(переписал merge-логику). Для Codex — добавляешь защиты +(валидации, gates, ограждения, untrusted-data обёртки). +Архитектурные фиксы меняют больше строк, но делаются один раз. +Защитные фиксы точечные, но их много. + +**Момент с intra-session vs cross-session.** В процессе возник +вопрос: "Ты хочешь сказать, твой teammate reviewer в процессе +работы, после каждого ответа, перезапускает сессию?" Нет — +внутри сессии Agent Teams сохраняют контекст нативно (teammate +живёт, получает SendMessage). Persistent memory решает другую +проблему: контекст между сессиями. Это важное различие, которое +не очевидно из описания фичи. + +**Plan Mode и запуск скиллов.** План создавался в Claude Code +Plan Mode. Потом из того же Plan Mode запустил `/claude-team-review` +— скилл корректно определил mode=plan из системного сообщения +"Plan mode is active". Затем `/adversarial-review` — тоже +определил plan mode. Оба скилла умеют работать из Plan Mode, +что удобно: ревьюишь план до выхода из планирования. + +## Что бы я сделал иначе + +Если повторять эксперимент: + +1. **Сразу параллельно.** Запускать оба ревьюера на одном входе + с самого начала, без последовательной фазы. +2. **Полный цикл для обоих.** Codex на v1 прошёл только 1 прогон. + Для честного сравнения — оба по 5 раундов с итерациями. +3. **Больше планов.** Один план — одна точка данных. Нужно 5-10 + разных планов разной сложности. +4. **Ревью кода, не только плана.** План — абстрактный артефакт. + Code review с реальным diff может показать другие паттерны. +5. **Фиксировать время.** Не записал сколько минут занял каждый + раунд. Для статьи это было бы полезно. ## Дата эксперимента