Делегировать в Codex CLI (read-only) одну из пяти задач - глубокий аудит/ревью кода, состязательное (adversarial) ревью со скептической позицией ("сломай решение"), независимое второе мнение по подходу/диффу, сверка реализации с исходным планом (compliance), или диагностику когда основной агент застрял. Использовать когда пользователь просит "отправь codex на аудит", "пусть codex проверит", "состязательное ревью", "попробуй сломать через codex", "второе мнение через codex", "посоветуйся с cod...
Installs into .claude/skills of the current project.
Are you the author of Codex Audit?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/dewil-codex-audit-ai-control)
---
name: codex-audit
description: Делегировать в Codex CLI (read-only) одну из пяти задач - глубокий аудит/ревью кода, состязательное (adversarial) ревью со скептической позицией ("сломай решение"), независимое второе мнение по подходу/диффу, сверка реализации с исходным планом (compliance), или диагностику когда основной агент застрял. Использовать когда пользователь просит "отправь codex на аудит", "пусть codex проверит", "состязательное ревью", "попробуй сломать через codex", "второе мнение через codex", "посоветуйся с codex", "проверь, что реализовал план", "сверь дифф с планом"; либо когда сам застрял (3+ провалившихся фикса, гипотезы при отладке не подтверждаются, делегированный субагент вернул BLOCKED) и нужна другая модель до похода к человеку; либо как вторую проверку после крупного изменения/фазы плана.
---
# codex-audit
Скилл для делегирования в `codex` CLI на этой машине задач, требующих **другой модели как независимого второго взгляда**, без права что-либо менять в репо (read-only). Codex изолирован: не видит контекст текущей сессии Claude, но читает файлы репозитория. Ценность не в том, что он "умнее" - а в **разнообразии мышления**: другая модель ловит другие классы ошибок, чем основной агент.
## Режимы
| Режим | Когда | Effort | Формат ответа Codex | Файлы в `docs/dev/` |
|---|---|---|---|---|
| `audit` (дефолт) | глубокий разбор кода/диффа/плана; "отправь на аудит", "пусть codex проверит" | `high` | по каждому вопросу вердикт ok / risk / blocker + 1-2 предложения | `audit-plan-<topic>` / `audit-report-<topic>` |
| `adversarial` | состязательное ревью со скептической позицией; "сломай решение", "состязательное ревью", "попробуй сломать через codex" - нужно не подтверждение, а активный поиск дыр | `high` | по каждому компоненту: чем и при каких условиях сломается; "проблем нет" - только с перечнем проверенных атак | `adversarial-plan-<topic>` / `adversarial-report-<topic>` |
| `second-opinion` | независимый взгляд на подход/решение/дифф; "второе мнение", "посоветуйся"; по умолчанию после крупной работы | `medium` | согласен / не согласен + альтернативы + трейдоффы (не чеклист блокеров) | `consult-plan-<topic>` / `consult-report-<topic>` |
| `compliance` | сверка реализации с исходным планом; "проверь, что реализовал план", "сверь дифф с планом" - что сделано, что пропущено, что добавлено сверх плана | `medium` | таблица "пункт плана -> статус (реализовано / частично / пропущено / отклонено) -> комментарий" + блок "сверх плана" | `compliance-plan-<topic>` / `compliance-report-<topic>` |
| `escalation` | основной агент застрял (3+ провалившихся фикса, гипотезы не подтверждаются, субагент BLOCKED) - до похода к человеку | `high` | диагноз + root cause с доказательствами + рекомендованный подход | `escalate-plan-<topic>` / `escalate-report-<topic>` |
Общая обвязка одинакова во всех режимах: запуск через `codex exec -s read-only`, фиксация `-C <project-root>`, модель `gpt-5.5`, pre-check и пост-сбор лимитов, блок про субагентов в плане, требование к Codex писать `session id` первой строкой отчета. Различаются только `effort`, фрейминг плана/промпта, формат ответа и имена файлов.
## Когда применять
- Пользователь явно просит передать что-то в codex ("отправь codex", "пусть codex посмотрит", "второе мнение", "посоветуйся с codex").
- Нужна независимая проверка плана/диффа/архитектурного решения второй моделью.
- Нужно **состязательное (adversarial) ревью** (режим `adversarial`): не "подтверди, что ок", а "исходи из того, что дефект есть, и найди, чем решение сломается". Зовется фразами "состязательное ревью", "сломай решение", "попробуй сломать через codex".
- Нужна **сверка кода с планом** (режим `compliance`): сравнить реализованный дифф с исходным планом - что сделано, что пропущено, что отклонилось, что добавлено сверх плана. Зовется фразами "проверь, что реализовал план", "сверь дифф с планом", "compliance check". Уместно после фазы плана, до закрытия задачи.
- Основной агент застрял (режим `escalation`): отладка зашла в тупик, делегированный субагент вернул BLOCKED, несколько фиксов подряд не сработали - зовем Codex до того, как идти к пользователю.
- **Second-opinion по умолчанию.** После крупного изменения или завершенной фазы плана уместно прогнать `second-opinion`, не дожидаясь явной просьбы - это часть нормального workflow. Но с оглядкой: сначала pre-check лимита, и не на каждую мелочь (см. "Когда НЕ применять"). Forcing-функции через hook здесь намеренно нет - дисциплину держит этот скилл, а не блокировка.
## Когда НЕ применять
- Любые изменения файлов/коммиты/деплой - codex в этом скилле всегда read-only. Фиксы по находкам применяет сам Claude в своей сессии, не Codex.
- Опечатки, пропущенные импорты, очевидные ошибки - не повод звать вторую модель.
- До того как сам провел первичное расследование - сначала разберись, потом эскалируй.
- Если задача мелкая и решается тут же без второго мнения - не плодим вызовы.
## Жесткие правила
1. **Всегда** запускать с `-s read-only`. Других режимов в этом скилле нет.
2. **Всегда** фиксировать рабочий каталог через `-C <project-root>`. В `-C` подставляй корень текущего проекта (абсолютный путь рабочего каталога), не хардкодь путь.
3. **Всегда** явно фиксировать модель и effort в команде, не полагаться на `~/.codex/config.toml`:
- `-c model="gpt-5.5"` - максимально качественная доступная модель, во всех режимах.
- `-c model_reasoning_effort="<effort>"` - по режиму: `high` для `audit`, `adversarial` и `escalation`, `medium` для `second-opinion` и `compliance` (доступно: minimal / low / medium / high / xhigh).
- **`minimal` не использовать** - ломается в codex-cli.
- `audit`, `adversarial` и `escalation` - глубокие задачи, нужна высокая глубина рассуждений (`high`). `second-opinion` намеренно легче (`medium`): ценность в стороннем взгляде, а не в исчерпывающей проверке. `compliance` тоже `medium` - это в основном механическая сверка пунктов плана с диффом, а не глубокий анализ.
4. **План** класть в `docs/dev/<plan-file>.md` (рабочая заметка, не едет на прод; имя файла - по таблице "Режимы").
5. **Отчет** писать в `docs/dev/<report-file>.md` через shell-редирект, потом читать через `Read`.
6. Bash-таймаут поднимать до 600000ms (10 мин) - codex может думать долго, особенно на `high`.
7. **Всегда** после сессии собирать статистику лимитов аккаунта (см. блок "Статистика лимитов после сессии") - дописывать в конец `<report-file>.md` и показывать пользователю в саммари. Без этого скилл считается выполненным некорректно: непонятно, насколько мы близко к ограничению по тарифу. Статистика по самим токенам (input/output/reasoning) намеренно не собирается - она шум, важны только проценты лимитов.
8. **Всегда** перед запуском codex делать pre-check короткого окна (`5h` / `primary`) лимита по последнему rollout (см. блок "Pre-check лимита перед запуском"). Если осталось `< 10%` - НЕ запускать codex; вернуть пользователю алерт с фактическим остатком и временем до сброса, и попросить решить (подождать или все равно запустить). Иначе мы тратим контекст и время на запуск, который завершится частичным ответом или ошибкой лимита. Если rollout-логов нет вообще (первый запуск на машине) - предупредить пользователя и разрешить запуск.
## Шаблон вызова
```bash
codex exec -s read-only \
-C <project-root> \
-c model="gpt-5.5" \
-c model_reasoning_effort="<effort>" \
"$(cat docs/dev/<plan-file>.md)" \
> docs/dev/<report-file>.md 2>&1
```
Легенда: `<effort>` - `high` (audit/adversarial/escalation) или `medium` (second-opinion/compliance); `<plan-file>` / `<report-file>` - имена из таблицы "Режимы"; `<project-root>` - абсолютный путь корня текущего проекта, в котором сейчас идет работа.
Альтернатива со stdin (если план уже в переменной/буфере):
```bash
codex exec -s read-only \
-C <project-root> \
-c model="gpt-5.5" \
-c model_reasoning_effort="<effort>" \
- < docs/dev/<plan-file>.md \
> docs/dev/<report-file>.md 2>&1
```
### Опционально: resume существующей сессии
Для повторного раунда по той же теме (re-review после применения фиксов, уточнение мутного ответа, догрузка вопросов) можно переиспользовать сессию Codex через `resume <SESSION_ID>` - тогда Codex не нужно заново скармливать план, файлы и фрейминг, он помнит контекст первого прохода. Экономит входной токен-бюджет и сохраняет преемственность анализа.
```bash
codex exec resume "$SESSION_ID" \
-C <project-root> \
"$(cat docs/dev/<followup-plan-file>.md)" \
> docs/dev/<followup-report-file>.md 2>&1
```
`SESSION_ID` берется из шапки предыдущего отчета (Codex по нашей просьбе пишет `session id: <id>` первой строкой - см. "Упаковка контекста в план"):
```bash
SESSION_ID=$(awk -F': ' '/^session id:/ {print $2; exit}' docs/dev/<previous-report-file>.md)
```
Особенности resume:
- `-c model="..."` и `-c model_reasoning_effort="..."` в resume **игнорируются** - resume наследует модель и effort исходной сессии. Это нормально для re-review того же режима; если нужна другая модель/effort - стартуй свежую сессию, не resume.
- Просьба про `session id` в новом плане **не нужна** - sessionID у продолженной сессии тот же; пост-сбор лимитов работает по тому же `SESSION_ID` (можно его же переиспользовать или достать из новой шапки, если Codex ее напишет).
- Pre-check лимита перед resume - такой же обязательный, как перед новым запуском: короткое окно могло быть исчерпано предыдущим раундом.
- Для каждой итерации используй отдельные имена файлов плана/отчета (`<plan-file>-r2.md`, `<report-file>-r2.md`, дальше `-r3` и т.д.), не перезаписывай предыдущие - история раундов нужна для разбора.
### Опционально: фоновый запуск для долгих аудитов
`codex exec` синхронный и на `high` может думать минутами, блокируя сессию Claude. Если есть чем заняться параллельно - запускать ту же команду в фоне через `run_in_background: true` у Bash-тула (не через `&`). Сессия не блокируется, по завершении придет нотификация; тогда: собрать статистику лимитов и прочитать отчет. Это нативный способ Claude Code - никаких плагинов/обвязок не нужно. Pre-check лимита делается до фонового запуска как обычно.
## Как codex выбирает модель
Порядок приоритета (от высшего к низшему):
1. `-m <model>` / `--model <model>` или `-c model="..."` в командной строке - переопределяет все.
2. `[profiles.<name>]` через `-p <profile>` - если указан профиль.
3. `~/.codex/config.toml` - глобальный дефолт, но опираться на него нельзя - конфиг может быть изменен между запусками.
В этом скилле мы **всегда** передаем модель через `-c` явно, чтобы качество вывода не зависело от состояния глобального конфига.
## Процесс
1. **Определить режим** (`audit` / `adversarial` / `second-opinion` / `compliance` / `escalation`) - от него зависят `effort`, фрейминг плана, формат ответа и имена файлов (таблица "Режимы").
2. **Pre-check лимита** (см. блок "Pre-check лимита перед запуском"). Если короткое окно (`5h` / `primary`) показывает `left < 10%` - НЕ запускать codex, вернуть пользователю алерт с остатком и временем до сброса, спросить как поступить. Если rollout-логов нет - предупредить и идти дальше.
3. **Сформулировать план** - записать в `docs/dev/<plan-file>.md`. План самодостаточный (Codex не видит историю чата): по чеклисту "Упаковка контекста в план". Фрейминг и формат ответа - под выбранный режим.
4. **Запустить codex** по шаблону выше, `timeout: 600000` (либо в фоне, см. "Опционально: фоновый запуск").
5. **Собрать статистику лимитов** (см. блок "Статистика лимитов после сессии") - дописать в конец `<report-file>.md`.
6. **Прочитать отчет** через `Read docs/dev/<report-file>.md`.
7. **Разобрать находки** (см. блок "Разбор находок") - верифицировать каждую, разделить на "применил / предлагаю / нужно решение". Кратко изложить пользователю (3-5 пунктов) + блок "лимиты" из двух строк (по одной на каждое окно `5h` и `1w`):
- `<окно>: left N% (used M%), reset [D day ]H:MM hour` (целые сутки + часы:минуты до конца окна);
- если по любому окну `left < 20%` - отдельной строкой над блоком вынести предупреждение "близко к лимиту по окну <окно>".
Спросить, какие пункты брать в работу. Не браться за все молча.
8. После завершения задачи решить судьбу файлов: если задача закрыта, можно удалить `<plan-file>` и `<report-file>` или сослаться на них из соответствующего todo.
## Упаковка контекста в план
Каждый вызов Codex изолирован - план обязан быть самодостаточным. Минимум (адаптируй под режим):
- **Проблема / цель** - 1-2 предложения: что проверяем (`audit`), что пытаемся сломать (`adversarial`), какое решение/подход оцениваем (`second-opinion`), что сверяем с планом (`compliance`), где застряли (`escalation`).
- **Контекст** - 2-5 строк: что за фича, почему сейчас этим занимаемся.
- **Ключевые файлы** - конкретные пути с номерами строк (Codex видит репо, но укажи куда смотреть), не общие "посмотри в app/". Для ревью большого PR/диффа НЕ вставляй в план полный патч - дай список `git diff --name-only <base>..HEAD` плюс 2-3 хот-спота с номерами строк; Codex сам подтянет нужное через `-C`. Это экономит контекст ревьюера и снижает риск упереться в окно.
- **Что уже пробовали** (особенно для `escalation`) - гипотезы/фиксы и почему не сработали.
- **Evidence** - точные ошибки, стектрейсы, вывод тестов.
- **Ожидаемый результат / формат ответа** - что значит "готово"; попросить отвечать структурно по формату режима (`audit` -> вердикт ok/risk/blocker; `adversarial` -> список атак с серьезностью, "проблем нет" только с перечнем проверенного; `second-opinion` -> согласие + альтернативы + трейдоффы; `compliance` -> таблица "пункт плана -> статус -> комментарий" + блок "сверх плана"; `escalation` -> диагноз + root cause + подход).
- **`session id` первой строкой** - попросить Codex первой строкой отчета написать `session id: <id>` (нужно для пост-сбора статистики лимитов).
- **Делегирование субагентам** - отдельным блоком в начале плана (шаблон ниже).
### Шаблон блока про субагентов
Вставлять в начало плана дословно или близко к тексту:
```text
## Использование субагентов
По возможности делегируй вспомогательную работу собственным субагентам на более дешевых моделях:
- поиск файлов/символов, grep по репо;
- чтение больших файлов и выборка релевантных фрагментов;
- сбор фактов (версии, конфиги, наличие/отсутствие чего-либо);
- проверка тривиальных условий ("есть ли тест на X", "используется ли функция Y").
Что НЕ делегировать дешевым моделям:
- финальные вердикты (ok / risk / blocker);
- рассуждения о безопасности, корректности логики, инвариантах;
- сводное заключение в конце отчета.
Главный критерий - качество итогового вывода. Если задача требует основной модели для сохранения качества - используй основную модель, не экономь.
```
## Шаблоны промптов
Подставить в `<plan-file>` (дополнив контекстом по чеклисту "Упаковка контекста"). Во всех режимах в конце - `READ-ONLY: только отчет, файлы не менять` и просьба про `session id` первой строкой.
### Аудит
```text
Аудит кода (read-only).
Цель: [что проверить - одна фраза]
Контекст: [2-5 строк]
Файлы: [пути с номерами строк]
Вопросы:
1. [...]
2. [...]
Формат: по каждому вопросу - вердикт ok / risk / blocker + 1-2 предложения обоснования.
READ-ONLY: только отчет, файлы не менять. Первой строкой отчета: session id: <id>.
```
### Состязательное (adversarial) ревью
```text
Состязательное (adversarial) ревью (read-only).
Ты adversarial-ревьюер. Исходи из того, что это решение СОДЕРЖИТ скрытый
дефект - твоя задача найти его, а не подтвердить корректность. Не одобряй,
ломай уверенность в решении.
Что ревьюим: [код / дифф / план / спецификация - что именно]
Контекст: [2-5 строк]
Файлы: [пути с номерами строк]
Для каждого компонента ответь:
- при каких входах / состояниях / гонках / нагрузке это сломается?
- какие инварианты и предусловия предполагаются, но не гарантируются?
- где автор рассчитывал на happy path и не закрыл отказ?
- какие неявные допущения рухнут при изменении окружения / данных / масштаба?
НЕ репортить (scope exclusions):
- стиль, нейминг, форматирование, докстринги, комментарии;
- спекулятивные улучшения ("можно было бы вынести в helper", "лучше переименовать", "стоит сделать конфигурируемым");
- повторы кода без functional impact;
- "не идиоматично" без конкретного риска;
- опечатки в комментариях / строках логов;
- предложения добавить тесты / документацию / телеметрию (это не атака).
Цель режима - только то, что может реально сломаться, открыть уязвимость или
привести к некорректному поведению. Шум снижает доверие к находкам.
Формат: список атак, по каждой - что атаковал, чем ломается (или почему
устояло), серьезность. Вердикт "проблем нет" допустим ТОЛЬКО если перечислил,
что конкретно проверял и почему атака не прошла.
READ-ONLY: только отчет, файлы не менять. Первой строкой отчета: session id: <id>.
```
### Второе мнение (по диффу/изменению)
```text
Независимое второе мнение (read-only).
Что оцениваем: [PR / ветка / незакоммиченные изменения / подход]
Файлы: [ключевые пути]
Что уже сделал основной агент: [кратко, или "проблем не нашел"]
Нужно: независимый взгляд - согласен ли с подходом, какие альтернативы, какие баги / edge-case / логические ошибки мог пропустить основной агент.
Формат: согласен / не согласен по пунктам + альтернативы + трейдоффы. Чеклист блокеров не нужен.
READ-ONLY: только отчет, файлы не менять. Первой строкой отчета: session id: <id>.
```
### Сверка кода с планом (compliance)
```text
Сверка реализации с исходным планом (read-only).
Исходный план:
[вставить план целиком ИЛИ путь к файлу плана в репо - тогда Codex прочитает сам]
Что реализовано:
[git ref/ветка/дифф ИЛИ список затронутых файлов с описанием объема; для большого диффа - `git diff --name-only <base>..HEAD` плюс 2-3 хот-спота с номерами строк]
Нужно: по каждому пункту плана определить статус - реализовано / частично / пропущено / отклонено (с пояснением, в чем отклонение). Дополнительно перечислить, что есть в диффе сверх плана (не было заявлено, но появилось).
Формат: таблица "пункт плана -> статус -> комментарий" + отдельный блок "сверх плана". Без оценок качества кода (это не аудит) - только сверка факта реализации.
READ-ONLY: только отчет, файлы не менять. Первой строкой отчета: session id: <id>.
```
### Эскалация при отладке
```text
Эскалация отладки (read-only).
Проблема: [1-2 предложения]
Файлы: [ключевые пути]
Проверенные гипотезы:
1. [гипотеза] -> [результат]
2. [гипотеза] -> [результат]
Evidence: [ошибка / вывод тестов / стектрейс]
Нужно: диагноз root cause с доказательствами + рекомендованный подход к фиксу.
READ-ONLY: только отчет, файлы не менять. Первой строкой отчета: session id: <id>.
```
### Заблокированный субагент
```text
Делегированный субагент застрял (read-only).
Задача: [название]
Блокер: [что субагент сообщил]
Контекст: [что требует задача]
Уже пробовали: [перезапуск с контекстом, апгрейд модели и т.д.]
Нужно: диагноз блокера + рекомендованный подход.
READ-ONLY: только отчет, файлы не менять. Первой строкой отчета: session id: <id>.
```
### Ревью плана / фазы
```text
Ревью плана перед выполнением (read-only).
Суть плана: [что собираемся делать - кратко]
Затрагиваемые файлы: [список]
Известные риски: [о чем уже думаешь]
Нужно: ревью на design-флоу, регрессии, проблемы интеграции, пропущенные шаги.
Формат: согласен / риски / блокеры по пунктам.
READ-ONLY: только отчет, файлы не менять. Первой строкой отчета: session id: <id>.
```
## Разбор находок
Codex не видит контекст сессии и **может ошибаться**. Не доверять слепо.
1. **Сначала независимо верифицируй каждую находку** в коде (Read/Grep) - Codex не видел контекст сессии, мог ошибиться. Пометь оценку: подтверждаю / сомневаюсь / не согласен.
2. **Технические находки** (баги, security, race, performance, API misuse) - оцени и применяй сам, в своей сессии (Codex здесь read-only, файлы не правит).
3. **К пользователю иди только за продуктовым решением**: бизнес-логика, scope ("баг или фича?"), приоритет, trade-off ("чинить сейчас или отложить?").
4. **В итоговом резюме пользователю раздели на три группы**: "проверил и применил / предлагаю / нужно твое решение".
5. **Хвост под режим**:
- `audit`: **blocker** - обсудить с пользователем, не править молча; **risk** - поднять в саммари, дать выбрать; **ok** - упомянуть одной строкой.
- `adversarial`: каждую найденную атаку верифицируй в коде - Codex со скептической установкой склонен к ложным тревогам (репортит дефект там, где инвариант на деле держится). Подтвержденные - в группы "применил / предлагаю"; неподтвержденные - отметь как проверенные и отклоненные, не тащи в саммари как реальные. **Опциональный re-review после фиксов:** adversarial-фиксы часто переписывают тонкие места (race, валидация, error handling), и сам фикс может породить новый дефект или закрыть атаку не полностью. После применения подтвержденных находок уместно дернуть второй проход Codex'а только на дифф фиксов через `codex exec resume <SESSION_ID>` (см. "Опционально: resume существующей сессии") - Codex помнит контекст исходного ревью и проверяет, что атаки реально закрыты и фиксы не сломали соседних инвариантов. Решение делать второй проход - **за пользователем, не молча**: после первого раунда предложи в саммари, перечислив, какие фиксы будут проверяться. Pre-check лимита перед re-review - обязательный.
- `second-opinion`: вынести согласие/расхождения и альтернативы; это advisory, а не чеклист на закрытие.
- `compliance`: пропуски и отклонения - к пользователю (продуктовое решение: доделать сейчас, отложить, зафиксировать в TODO, признать осознанным отклонением). Блок "сверх плана" - проверить самостоятельно: это могло быть оправданное доработкой, а могло быть scope creep; подсветить пользователю при втором случае.
- `escalation`: верифицировать диагноз прежде чем строить фикс; если root cause подтвердился - применить рекомендованный подход; если Codex не помог - эскалировать к пользователю, привести findings обоих.
6. Если codex ушел в сторону или дал слишком общий ответ - уточнить план и перезапустить, не принимать мутный ответ как готовый.
## Pre-check лимита перед запуском
Цель: не запускать `codex exec`, если короткое окно (`primary`, у plus-плана это **5h**) на тарифе кончается. Иначе мы тратим минуты ожидания и контекст на ответ, который codex оборвет на полпути по rate-limit.
Источник данных - тот же rollout-лог, что и в "Статистика лимитов после сессии". Берем **последний** rollout-файл по mtime (не привязываемся к session id - его еще нет, мы только собираемся запускать новую сессию). В нем находим последний `token_count` event и из `rate_limits.primary` берем `used_percent` и `resets_at`.
Порог: **`left < 10%`** в окне `primary` - блокировать запуск. Цифра намеренно меньше порога "близко к лимиту" из саммари (20%): 10-20% хватит на средний аудит, а ниже 10% - уже почти гарантированно оборвет.
### Команда (запускать до `codex exec`)
```bash
ROLLOUT=$(ls -1t ~/.codex/sessions/*/*/*/rollout-*.jsonl 2>/dev/null | head -1)
if [ -z "$ROLLOUT" ]; then
echo "PRE_CHECK: no rollout logs found - first codex session on this machine, proceed with warning"
else
python3 - "$ROLLOUT" <<'PY'
import json, sys, datetime
def fmt_until(s):
s = int(s)
if s < 60: return f"{s} sec"
days = s // 86400
rem = s - days * 86400
hours = rem // 3600
mins = (rem - hours * 3600) // 60
if days: return f"{days} day {hours}:{mins:02d} hour"
if hours: return f"{hours}:{mins:02d} hour"
return f"{mins} min"
last = None
for line in open(sys.argv[1]):
try:
o = json.loads(line)
p = o.get('payload') or {}
if p.get('type') == 'token_count' and (p.get('rate_limits') or {}).get('primary'):
last = p
except Exception:
pass
if not last:
print("PRE_CHECK: no rate_limits event in latest rollout - proceed with warning")
sys.exit(0)
pr = last['rate_limits'].get('primary') or {}
used = pr.get('used_percent', 0)
left = max(0.0, 100.0 - used)
rt = pr.get('resets_at')
if isinstance(rt, str):
try:
rt = datetime.datetime.fromisoformat(rt.replace('Z','+00:00')).timestamp()
except Exception:
rt = None
now = datetime.datetime.now(datetime.timezone.utc).timestamp()
reset = fmt_until(max(0, rt - now)) if rt else "unknown"
if left < 10:
print(f"PRE_CHECK: BLOCKED - 5h window left {left:.0f}% (used {used:.0f}%), reset {reset}")
else:
print(f"PRE_CHECK: ok - 5h window left {left:.0f}% (used {used:.0f}%), reset {reset}")
PY
fi
```
### Как обрабатывать результат
- `PRE_CHECK: ok ...` - продолжить, запустить codex.
- `PRE_CHECK: BLOCKED ...` - НЕ запускать codex. Вернуть пользователю строку как есть + спросить: "ждать сброса или запустить все равно (рискуя оборваться)?". Не самочинно ставить таймер - решение пользователя. Если пользователь явно скажет "запускай" - запустить, иначе ждать.
- `PRE_CHECK: no rollout ...` / `no rate_limits event ...` - предупредить пользователя ("данных о лимите нет, запускаю") и идти дальше.
### Почему только короткое окно (5h)
Длинное окно (`1w` / `secondary`) тоже отдается в rollout, но один аудит за пару минут не сожжет неделю - там обычно остаток с большим запасом. Блокировать по `secondary` смысла нет: пользователю придется ждать дни до сброса. Если когда-нибудь окажемся в ситуации `secondary < 10%` - вынести отдельно в саммари как warning, но не блокировать запуск.
## Статистика лимитов после сессии
Codex CLI пишет лог сессии в `~/.codex/sessions/<YYYY>/<MM>/<DD>/rollout-<ts>-<session_id>.jsonl`. Внутри в событиях `token_count` есть блок `rate_limits` с двумя окнами:
- `primary` - короткое окно (для plus-плана `window_minutes=300`, то есть **5h**).
- `secondary` - длинное окно (для plus-плана `window_minutes=10080`, то есть **1w**).
Каждое окно содержит `used_percent` (сколько уже потрачено) и `resets_at` (unix-timestamp, когда счетчик обнулится).
Сами токены (input/cached/output/reasoning) намеренно НЕ собираются - в этом скилле важно одно: насколько мы близко к ограничениям тарифа и когда они сбросятся.
> **Почему не через `/status`.** Слаш-команда `/status` в TUI Codex показывает лимиты, но через `codex exec "/status"` она не работает - модель воспринимает строку как обычный prompt и отвечает текстом про workdir/sandbox без чисел. Отдельного `codex status` subcommand-а тоже нет (`codex --help` не содержит). Поэтому стат собирается из локального rollout-лога, который codex пишет автоматически.
### Команда (запускать сразу после codex exec)
`<report-file>` - отчет твоего режима из таблицы "Режимы" (`audit-report-<topic>` / `consult-report-<topic>` / `escalate-report-<topic>`). Извлечь `session id` из шапки только что созданного отчета, найти соответствующий rollout-файл, достать последний `rate_limits` и дописать блок в конец отчета:
```bash
SESSION_ID=$(awk -F': ' '/^session id:/ {print $2; exit}' docs/dev/<report-file>.md)
if [ -z "$SESSION_ID" ]; then
ROLLOUT=""
else
ROLLOUT=$(ls -1t ~/.codex/sessions/*/*/*/rollout-*-${SESSION_ID}.jsonl 2>/dev/null | head -1)
fi
{
echo
echo "## Rate limits"
echo
if [ -z "$ROLLOUT" ]; then
if [ -z "$SESSION_ID" ]; then
echo "no session id in report header - rate limits not collected"
else
echo "no rollout file for session $SESSION_ID - rate limits not collected"
fi
else
python3 - "$ROLLOUT" <<'PY'
import json, sys, datetime
def fmt_window(m):
if m % 10080 == 0: return f"{m//10080}w"
if m % 1440 == 0: return f"{m//1440}d"
if m % 60 == 0: return f"{m//60}h"
return f"{m}min"
def fmt_until(s):
s = int(s)
if s < 60: return f"{s} sec"
days = s // 86400
rem = s - days * 86400
hours = rem // 3600
mins = (rem - hours * 3600) // 60
if days: return f"{days} day {hours}:{mins:02d} hour"
if hours: return f"{hours}:{mins:02d} hour"
return f"{mins} min"
last = None
with open(sys.argv[1]) as f:
for line in f:
try:
o = json.loads(line)
p = o.get('payload') or {}
if p.get('type') == 'token_count' and (p.get('rate_limits') or {}).get('primary'):
last = p
except Exception:
pass
if not last:
print("no rate_limits event found"); sys.exit(0)
rl = last['rate_limits']
now = datetime.datetime.now(datetime.timezone.utc).timestamp()
for key in ('primary', 'secondary'):
w = rl.get(key) or {}
if not w: continue
label = fmt_window(w.get('window_minutes', 0))
used = w.get('used_percent', 0)
left = max(0.0, 100.0 - used)
rt = w.get('resets_at')
if isinstance(rt, str):
try:
rt = datetime.datetime.fromisoformat(rt.replace('Z','+00:00')).timestamp()
except Exception:
rt = None
if rt:
secs = max(0, rt - now)
print(f"{label:>4}: left {left:.0f}% (used {used:.0f}%), reset {fmt_until(secs)}")
else:
print(f"{label:>4}: left {left:.0f}% (used {used:.0f}%)")
PY
fi
} >> docs/dev/<report-file>.md
```
Пример того, что получается в конце отчета:
```text
## Rate limits
5h: left 98% (used 2%), reset 2:35 hour
1w: left 99% (used 1%), reset 1 day 2:35 hour
```
### Как читать
- `left` - сколько процента квоты тарифа доступно сейчас. Если `< 20%` (для любого из двух окон) - вынести в саммари отдельной строкой "близко к лимиту по окну <5h|1w>".
- `reset [D day ]H:MM hour` - компактный таймер до конца текущего окна: целые сутки (если есть), затем часы:минуты с ведущим нулем у минут. Примеры: `2:35 hour`, `1 day 0:00 hour`, `7 day 4:12 hour`, `45 min` (когда меньше часа), `30 sec` (когда меньше минуты). Окна скользящие: `resets_at` в исходных данных - это конец **текущего** окна, не "ровно через 5 часов от сейчас".
- Окна берутся из `window_minutes`. Лейблы `5h` / `1w` подставляются автоматически (300min -> `5h`, 10080min -> `1w`); если codex когда-нибудь сменит тариф/окна - в отчете появятся новые лейблы автоматически, скилл переписывать не надо.
### Если статистика не нашлась
- Файл rollout может не появиться, если codex упал на старте (например, проблемы с авторизацией) или сессия запущена с `--ephemeral`. В этом случае команда напечатает `no rollout file for session <id>` либо (если в шапке отчета нет `session id:`) `no session id in report header`. Это безопаснее, чем подцеплять чужой rollout - статистики просто не будет, и пользователь увидит причину.
- Не использовать `--ephemeral` в этом скилле - теряется возможность собрать статистику.
- Если в шапке отчета нет строки `session id:` - проверь сам план: по чеклисту "Упаковка контекста в план" Codex должен первой строкой отчета писать `session id: <id>`. Без этой строки пост-сборка статистики не работает.
## Ограничения
- Codex не получает контекст текущего чата, плана Claude Code, открытых файлов - только то, что в плане и что прочитает сам в репо.
- Read-only означает: codex не правит файлы, не коммитит, не запускает разрушительные команды. Но он **может** читать любые файлы внутри `-C` каталога, включая `.env` если тот есть. Не пихать в план секреты - они попадут в `<plan-file>.md` (рабочая заметка, в git не едет, но локально лежит).
- Разрешение в `settings.local.json` строго `Bash(codex exec -s read-only:*)` - без этого префикса вызов потребует подтверждения.
- Скилл намеренно использует синхронный `codex exec`, а не async-плагин `codex-plugin-cc` (`codex-companion.mjs`). Async дает non-blocking, но он Linux-only (нужны `/proc`, `flock`, GNU coreutils) и тащит классы зависаний (зомби-джобы, заклинивший per-cwd broker - в цепочке worker->broker->app-server нет таймаута на запрос), для лечения которых нужна отдельная обвязка. У синхронного `codex exec` этих проблем нет: один foreground-процесс, Bash-таймаут чисто его снимает, чистить нечего. Non-blocking при необходимости берется нативным `run_in_background` (см. "Опционально: фоновый запуск").