Добавляем в режим Codex Plan журнал решений codex-plan-ledger: фиксируем выбор и проверяем отклонения от плана
codex-plan-ledger записывает решения, принятые в режиме Codex Plan (/plan), в файл docs/plans/<id>/decisions.json. Этот файл попадает в тот же PR, что и код, его можно диффить и ревьюить, а команда plan-ledger check --base main сопоставляет изменённые файлы с планом и показывает, что вышло за рамки плана и какие запланированные файлы остались нетронутыми.
На парных экспериментах автор показывает, что такой журнал решений в Codex Plan помогает выявлять отклонения от плана, но число раундов и токенов стабильно не улучшается.
Когда пишешь код в режиме Plan в Codex (/plan), самое ценное — зачастую не сам план, а те несколько решений, которые были приняты по ходу его составления: повторять ли попытку при сбое, задавать конфигурацию через параметры или через переменные окружения, какие файлы трогать нельзя. Эти решения обычно остаются только в переписке. Посмотришь на PR через две недели — ревьюер видит код, но не видит, «почему тогда выбрали именно так»; и сверить, не вышло ли код за изначально оговорённые рамки, тоже не с чем.
codex-plan-ledger делает ровно две вещи:
- Журнал решений. Записывает решения из режима Plan в
docs/plans/<id>/decisions.jsonв репозитории: вопросы, которые Codex задал вам, и ваши ответы, ещё не определённые варианты из плана, явно указанные в плане значения по умолчанию — всё там. Он лежит в одном PR с кодом, его можно диффить и ревьюить. - Проверка выхода за рамки. Когда код написан,
plan-ledger check --base mainсверяет изменённые файлы с планом: файлы, изменённые вне плана, и файлы, перечисленные в решениях, но так и не тронутые, — всё это выводится в отчёт.
Как это работает

Как пользоваться
npm i -g github:miniLV/codex-plan-ledger
plan-ledger init --write # 写入 Codex hooks 配置
Дальше как обычно /plan в Codex, и на вопросы Codex отвечаете там же, как обычно. Когда план готов, Stop hook делает три вещи: разбирает план, пишет журнал, генерирует одностраничный офлайн-HTML. В терминале появляется лишняя строка, например:
plan-ledger: nothing to answer; 2 answered in Codex → file://…/plan.html
На этой странице можно проверить все решения: то, на что уже ответили в Codex, помечается «отвечено в Codex»; явно указанные в плане значения по умолчанию помечаются «по умолчанию, можно проверить» и не считаются пунктами, ожидающими ответа; нерешённые пункты выбираются все сразу, по кнопке генерируется JSON для отправки, который вставляется в следующее сообщение. Невыбранные пункты записываются как «без ответа, сохраняется значение по умолчанию», это не считается согласием.
Когда код написан:
plan-ledger check --base main # 可读报告
plan-ledger check --base main --strict # 有偏离时 exit 1,可以放进 CI
Несколько ограничений по дизайну:
- Рендеринг целиком на чистых шаблонах, без дополнительных вызовов модели, без зависимостей, один HTML-файл, без сети.
- hook никогда не блокирует этот раунд и не ждёт вас. Если формат плана не распознан, пропускает как есть, добавляя лишь одну подсказку.
- Ответы на странице хранятся только как данные и не исполняются.
Как выглядит журнал
{
"id": "n-selection",
"title": "Selection",
"question": "How should users select the greeting language?",
"source": "codex-native", // Codex 自己问的,你在 Codex 里答的
"options": [
{ "id": "a", "label": "Named flag", "recommended": true },
{ "id": "b", "label": "Second positional argument", "recommended": false },
{ "id": "c", "label": "Environment variable", "recommended": false }
],
"chosen": "a",
"status": "answered"
}
Этот фрагмент — из реальной сессии режима Plan (codex-cli 0.156.0): Codex задал 2 вопроса через request_user_input, реальный Stop hook вычитал вопросы и ответы из записи сессии и записал их в журнал. Вся эта сессия — около 43000 токенов. Нужно оговориться: в этот раз в промпте явно требовалось, чтобы Codex сначала задавал вопросы, — это проверка функциональности, а не измерение эффекта.
На практике: честно говоря, число раундов не уменьшилось
Изначально задумывалось, что «ответить на все решения на одной странице за раз удобнее, чем когда Codex спрашивает раунд за раундом». Я провёл парный эксперимент на vercel/[email protected]: одна и та же задача, одна и та же модель и настройки, нативный режим Plan и режим с plan-ledger прогоняются по разу, на вопросы отвечает скрипт по скрытому эталонному ответу. Всего два раунда, 2 задачи, n = 3 пары, это можно считать лишь ориентировочными данными:
| Раунд / задача | Раундов до финального варианта (нативный / plan-ledger) | Всего токенов (нативный / plan-ledger) | Приёмка |
|---|---|---|---|
| 1 / strict-option | 1 / 2 | 717,487 / 586,971 | все прошли |
| 2 / strict-option | 1 / 2 | 180,208 / 205,644 | все прошли |
| 2 / month-unit | 1 / 1 | 179,053 / 140,020 | все прошли |
- Число раундов не улучшилось: из 3 пар проиграл в 2, ничья в 1. По токенам где больше, где меньше, единого направления нет (в двух раундах конфигурации запуска разные, токены можно сравнивать только внутри одного раунда).
- Выход за рамки пойман в 3/3: после реализации в копию подложили новые файлы вне плана и вернули файлы из плана в исходное состояние — во всех 3 прогонах это было выведено в отчёт; на чистом варианте в 3/3 ложных срабатываний не было.
- Изменения внутри файла, противоречащие решениям, пойманы в 0/3: если внутри файла из плана написать код, противоположный выбранному варианту, проверка этого не обнаружит. Она смотрит только «какие файлы изменены», но не читает содержимое кода — это известное ограничение.
Поэтому позиционирование проекта сместилось с «ответить на одной странице и сэкономить раунды» на нынешнее «журнал решений + проверка выхода за рамки». Эксперимент вскрыл ещё два момента, которые уже исправлены: режим Plan в Codex, даже когда его просят перечислить решения, сначала задаёт вопросы через встроенный request_user_input, поэтому теперь эти вопросы и ответы сразу записываются в журнал и повторно не задаются; «Chosen defaults» в конце плана раньше считались пунктами, ожидающими ответа, а теперь записываются как значения по умолчанию, доступные для проверки, и не считаются ожидающими ответа. После этих изменений замеры ещё не повторялись.
Полные данные и скрипты лежат в репозитории: раунд 1, раунд 2, план замеров.
Текущее состояние и что дальше
Ранний прототип v0.1, с покрытием тестами. Экспериментальные части: UserPromptSubmit hook и чтение нативных вопросов и ответов из записи сессии (формат записи сессии — не публичный контракт, если прочитать не удаётся, шаг пропускается). Windows не тестировался.
Дальше:
- Сверять решения по содержимому (то есть тот самый 0/3 выше).
- Больше задач, минимум 3 повторов на группу, измерить, насколько журнал покрывает решения в одной сессии и как работает обнаружение отклонений.
Пробуйте, заводите issue: github.com/miniLV/code… · главная страница и онлайн-демо: minilv.github.io/codex-plan-…
Идейно вдохновлялся html-plan и answer-me-with-html, код не переиспользовал.
Источник: Juejin · ИИ-программирование · juejin.cn
